# 题目

编写代码,移除未排序链表中的重复节点。保留最开始出现的节点。

示例 1:

输入:[1, 2, 3, 3, 2, 1]
 输出:[1, 2, 3]

示例 2:

输入:[1, 1, 1, 1, 2]
 输出:[1, 2]

tips:

  • 链表长度在 [0, 20000] 范围内。
  • 链表元素在 [0, 20000] 范围内。

# 解法一:HashSet

用 hashset 缓存已经遍历过的结点,在每次拼接下一个节点时,去判读是否已经遍历过这个数字,如果遍历过就跳过,如果没有就拼接在 res 链表上。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode removeDuplicateNodes(ListNode head) {
        if(head == null) return null;
        HashSet<Integer> hash = new HashSet<Integer>();
        ListNode pos = head;
        ListNode res = new ListNode(-1);
        ListNode cur = res;
        while(pos !=null){
            if(!hash.contains(pos.val)){
                 hash.add(pos.val);
                 ListNode node = new ListNode(pos.val);
                 cur.next = node;
                 cur = cur.next;
            }
           
            pos = pos.next;
        }
        return res.next;
    }
}
/********************* 稍微改进 ************************************/
class Solution {
    public ListNode removeDuplicateNodes(ListNode head) {
        if(head == null) return null;
        HashSet<Integer> hash = new HashSet<Integer>();
        ListNode pos = head;
        ListNode res = new ListNode(-1);
        ListNode cur = res;
        while(pos !=null){
            if(!hash.contains(pos.val)){
                 hash.add(pos.val);
                // ListNode node = new ListNode(pos.val);
                ListNode node = pos;
                 cur.next = node;
                 cur = cur.next;
            }
           
            pos = pos.next;
        }
        cur.next = null;
        return res.next;
    }
}
/********************* 官方题解 ************************************/
class Solution {
    public ListNode removeDuplicateNodes(ListNode head) {
        if (head == null) {
            return head;
        }
        Set<Integer> occurred = new HashSet<Integer>();
        occurred.add(head.val);
        ListNode pos = head;
        // 枚举前驱节点
        while (pos.next != null) {
            // 当前待删除节点
            ListNode cur = pos.next;
            if (occurred.add(cur.val)) {
                pos = pos.next;
            } else {
                pos.next = pos.next.next;
            }
        }
        pos.next = null;
        return head;
    }
}

时间复杂度: O (n),遍历链表一次

空间复杂度:O(N)

# 解法二:进阶

考虑题目描述中的「进阶」部分,是否存在一种不使用临时缓冲区(也就是方法一中的哈希表)的方法呢?

不幸的是,在保证方法一时间复杂度 O (N) O (N) 的前提下,是不存在这样的方法的。因此我们需要增加时间复杂度,使得我们可以仅使用常数的空间来完成本题。一种简单的方法是,我们在给定的链表上使用两重循环,其中第一重循环从链表的头节点开始,枚举一个保留的节点,这是因为我们保留的是「最开始出现的节点」。第二重循环从枚举的保留节点开始,到链表的末尾结束,将所有与保留节点相同的节点全部移除。

方法二的细节部分与方法一类似。第一重循环枚举保留的节点本身,而为了编码方便,第二重循环可以枚举待移除节点的前驱节点,方便我们对节点进行移除。这样一来,我们使用「时间换空间」的方法,就可以不使用临时缓冲区解决本题了。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode removeDuplicateNodes(ListNode head) {
        ListNode ob = head;
        while (ob != null) {
            ListNode oc = ob;
            while (oc.next != null) {
                if (oc.next.val == ob.val) {
                    oc.next = oc.next.next;
                } else {
                    oc = oc.next;
                }
            }
            ob = ob.next;
        }
        return head;
    }
}

时间复杂度: 其中 O (N2) 其中 N 是给定链表中节点的数目。

空间复杂度: O(1).