编写代码,移除未排序链表中的重复节点。保留最开始出现的节点。
示例1:
输入:[1, 2, 3, 3, 2, 1]
输出:[1, 2, 3]
示例2:
输入:[1, 1, 1, 1, 2]
输出:[1, 2]
提示:
链表长度在[0, 20000]范围内。
链表元素在[0, 20000]范围内。
进阶:
如果不得使用临时缓冲区,该怎么解决?
思路:我们最容易想到的就是用集合去重,从链表的头开始遍历,如果在HashSet集合中有出现重复的元素,我们直接过滤(删除)掉,时间复杂度为O(N)。
链表节点定义
public class ListNode {
//节点存储的数据
int val;
//节点的后继节点
ListNode next;
//构造方法,生成一个节点时要存入一个数据
public ListNode(int val) {
this.val = val;
}
}
方法实现与主方法测试
import java.util.HashSet;
import java.util.Set;
class Solution {
public ListNode removeDuplicateNodes(ListNode head) {
//pre指向当前节点的前驱
ListNode pre = null;
ListNode p = head;
Set set = new HashSet<>();
//遍历节点
while (p != null) {
//如果哈希表中已存在这个元素则删除
if (set.contains(p.val)) {
//删除,删除后前驱节点不需要往后移
pre.next = p.next;
} else {
//如果哈希表中不存在这个元素则添加进来
set.add(p.val);
//未删除,前驱节点后移
pre = p;
}
//无论是否删除都让p指向下一个待检测的节点
p = p.next;
}
return head;
}
public static void main(String[] args) {
//定义一个测试样例
int arr[] = {1, 2, 3, 3, 2, 1};
//将数组元素依次存入链表
ListNode head = new ListNode(1);
int i = 1;
ListNode p1 = head;
while (i < arr.length) {
p1.next = new ListNode(arr[i]);
p1 = p1.next;
++i;
}
//调用方法测试
new Solution().removeDuplicateNodes(head);
//输出样例的预期结果
ListNode p2 = head;
while (p2 != null) {
System.out.print(p2.val + (p2.next == null ? "" : " "));
p2 = p2.next;
}
}
}
对于进阶问题:如果不得使用临时缓冲区,该怎么解决?一种简单的方法是,我们在给定的链表上使用两重循环,其中第一重循环从链表的头节点开始,枚举一个保留的节点,这是因为我们保留的是「最开始出现的节点」。第二重循环从枚举的保留节点开始,到链表的末尾结束,将所有与保留节点相同的节点全部移除,时间复杂度为O(N^2)。
方法实现
public ListNode removeDuplicateNodes(ListNode head) {
//first节点指向每一轮比较的第一个节点
ListNode first = head;
while (first != null) {
//scanner节点为每一轮第一个节点之后的节点
//并依次与first节点比较,如果元素值相同则过滤掉
ListNode scanner = first.next;
//scanner节点的前驱节点
ListNode pre = first;
while (scanner != null) {
if (first.val == scanner.val) {
pre.next = scanner.next;
} else {
pre = scanner;
}
//让scanner指向下一个节点并与first节点比较
scanner = scanner.next;
}
//依次选取下一个节点作为第一个节点
first = first.next;
}
return head;
}