面试题 02.01. 移除重复节点
题目
编写代码,移除未排序链表中的重复节点。保留最开始出现的节点。
示例1:
- 输入:[1, 2, 3, 3, 2, 1]
- 输出:[1, 2, 3]
示例2:
- 输入:[1, 1, 1, 1, 2]
- 输出:[1, 2]
提示:
链表长度在[0, 20000]范围内。
链表元素在[0, 20000]范围内。
思路
暴力解法
- 从前面每个节点开始,与后面的每个节点进行比较,从而进行重复元素的移除,如下:
但是显然时间复杂度为O( n 2 n^2 n2) - 利用数组存储已访问节点,然后遇到下一个节点时判断是否在已访问数组中,当前数组可以用List来替代,同时使用contain方法判断是否该节点已访问,但是contain方法也是一个时间复杂度为O( n n n)的函数,所以该思路复杂度依旧为O( n 2 n^2 n2)
哈希表
利用哈希表来保存已访问数组,并使用map.get(p.next.val)!=null判断该节点是否存在哈希表中,可能有同学会有疑惑,为什么同样是存储已访问的值,但是哈希表却优于数组、List,其中主要是通过哈希函数的哈希映射能够以远小于O( n n n)的时间复杂度找到该节点,更甚者时间复杂度为O(1)。至于这方面的知识我就不展开来说,毕竟包含的知识点太多了
Java实现
我自己写的
class Solution {
public ListNode removeDuplicateNodes(ListNode head) {
Map<Integer,Boolean> map = new HashMap<>();
if (head==null||head.next==null) return head;
ListNode p = head;
map.put(p.val,Boolean.TRUE);
while (p!=null&&p.next!=null){
if (map.get(p.next.val)!=null) p.next = p.next.next;
else {
map.put(p.next.val,Boolean.TRUE);
p = p.next;
}
}
return head;
}
}
leetcode官方题解
为什么我会把官方的题解贴在这里,其实主要是刚好有个对照,同学们认真看,可以看到我的答案中使用的是HashMap,而官方题解使用的是HashSet,这其中有什么关系呢?其实它们都是哈希表,这个也是我一开始没有想到的,但是实质上,在这里确实是HashSet更好。在我的代码中,其实大家可以发现,判断哈希表中是否已存在该元素,我使用的是map.get(p.next.val)!=null,所以其实我存储的值是没有意义的。而在官方题解中,则是判断元素是否添加成功来判断哈希表是否存在该元素。显然,这灵活地运用了add函数的返回值,为什么返回值为false,那还不是已经存在该元素了吗?
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;
}
}