文章目录

  • 题目描述
  • 方法一:顺序删除
  • 算法性能分析
  • 方法二:递归法
  • 算法性能分析
  • 方法三:空间换时间


题目描述

给定一个没有排序的链表,去掉其重复项,并保留原顺序,如链表1→3→1→5→5→7,去掉重复部分后为1→3→5→7。

方法一:顺序删除

主要思路:通过双重循环直接在链表上执行删除操作。外层循环用一个指针从第一个结点开始遍历整个链表,然后内层循环用另外一个指针遍历其余结点,将与外层循环遍历到的指针所指结点的数据域相同的节点删除。

class LNode {
    /**
     * 数据域
     */
    int data;
    /**
     * 下一个结点的引用
     */
    LNode next;
}
public class Test2 {

    public static void removeDup(LNode head){
        /**
         * @Author: JavaRecord
         * @Description:对带头结点的无序单链表删除重复的结点:顺序删除
         * @Param [args]
         * @Return void
         * @Date 2020/8/10
         * @Time 17:01
         */
        if(head == null || head.next == null){
            return;
        }
        //用于外层循环,指向链表第一个结点
        LNode outerCur = head.next;
        //内层循环用来遍历outerCur后面的结点
        LNode innerCur = null;
        //innerCur的前驱结点
        LNode innerPre = null;
        for(;outerCur!=null;outerCur=outerCur.next){
            for(innerCur=outerCur.next,innerPre=outerCur;innerCur!=null;innerCur=innerCur.next){
                //找到重复的结点并删除
                if(outerCur.data==innerCur.data){
                    innerPre.next=innerCur.next;
                }else{
                    innerPre=innerCur;
                }
            }
        }
    }

    public static void main(String[] args) {
        int i = 1;
        LNode head = new LNode();
        head.next = null;
        LNode tmp = null;
        LNode cur = head;
        for(;i<7;i++){
            tmp = new LNode();
            if(i%2==0){
                tmp.data=i+1;
            }else if(i%3==0){
                tmp.data=i-2;
            }else {
                tmp.data = i;
            }
            tmp.next = null;
            cur.next = tmp;
            cur = tmp;
        }
        System.out.print("删除重复结点前:");
        for(cur=head.next;cur!=null;cur=cur.next){
            System.out.print(cur.data+" ");
        }
        removeDup(head);
        System.out.print("\n删除重复结点后:");
        for(cur=head.next;cur!=null;cur=cur.next){
            System.out.print(cur.data+" ");
        }
    }
}

程序运行结果:

删除重复结点前:1 3 1 5 5 7 
删除重复结点后:1 3 5 7

算法性能分析

双重循环,时间复杂度O(N^2),N为链表长度

遍历过程中,使用常数个额外的指针来保存当前遍历的结点、前驱结点和被删除的结点,因此,空间复杂度O(1)

方法二:递归法

主要思路:对于结点cur,首先递归删除以cur.next为首的子链表中重复的结点,接着以cur.next为首的子链表中找出与cur有着相同数据域的结点并删除。

private static LNode removeDupRecursion(LNode head){
        if(head.next==null){
            return head;
        }
        LNode pointer = null;
        LNode cur =head;
        //以对head.next为首的子链表删除重复的结点
        head.next = removeDupRecursion(head.next);
        pointer = head.next;
        //找出以head.next为首的子链表中与head结点相同的结点并删除
        while (pointer!=null){
            if(head.data==pointer.data){
                cur.next=pointer.next;
                pointer = cur.next;
            }else{
                pointer = pointer.next;
                cur = cur.next;
            }
        }
        return head;
    }

    public static void removeDup2(LNode head){
        /**
         * @Author: JavaRecord
         * @Description:对带头结点的单链表删除重复结点(递归法)
         * @Param [head]
         * @Return void
         * @Date 2020/8/10
         * @Time 17:52
         */
        if(head==null){
            return;
        }
        head.next = removeDupRecursion(head.next);
    }

算法性能分析

这种方法与方法一类似,从本质上讲,由于这种方法需要对链表进行双重遍历,因此,时间复杂度O(N^2)。其中,N为链表的长度。由于递归会增加许多额外的函数调用,所以从理论上讲,该方法效率比方法一低

方法三:空间换时间

使用辅助空间,主要思路:

(1)建立一个HashSet,HashSet中的内容为已经遍历过的结点内容,并将其初始化为空。

(2)从头开始遍历链表中的所有结点,存在以下两种可能:

① 结点已在HashSet中,则删除此结点,继续向后遍历

② 结点内容不在HashSet中,则保留此结点,将此结点内容添加到HashSet中,继续向后遍历