83. 删除排序链表中的重复元素
本文,仅用作自我记录,方便查看
本题有多种解法。
我的解法是最容易想到的,也是最简单的——单指针法(和后面的做区分)
1. 单指针法
由于该链表已经是有序的(按照val排序的),那么值一样的结点都是放在一起的
我们只需要有两个指针,一个指向cur、一个指向next:
-
cur != next(是指值),cur = cur.next
-
cur == next(是指值),cur = cur.next.next
注意❗️删除之后,cur不能指向next,因为cur和最新的next还没有进行比较
eg:[1,1,1] :cur = 第一个,删除重复的next之后,变成[1,1];此时,cur应该仍指向第一个,然后再次比较后删除next,变成了[1]
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode deleteDuplicates(ListNode head) {
if(head == null || head.next == null) return head; // 边界条件
ListNode cur = head;
while(cur != null && cur.next != null){ // 可能cur.next和cur重复了,删除next之后,cur就是最后一个结点了
ListNode next = cur.next;
if(cur.val == next.val){
cur.next = next.next;
}
else{
cur = cur.next;
}
}
return head;
}
}
还有一个解法:
2. 双指针
是一种更通俗的解法,用来解其他的去重题
参考:https://leetcode-cn.com/problems/remove-duplicates-from-sorted-list/solution/yuan-di-qu-chu-de-zhong-fu-yuan-su-yi-wen-miao-s-2/(这篇质量真高)
思路:
设置两个指针slow、fast
- slow:指向的是,遍历到当前时,最新去重后的链表尾部
- fast:用来遍历链表的
从头开始遍历,那么存在两种情况:
带入例子:[1,1,2,3,3]
-
slow和fast指向的结点值一样,那么fast继续遍历,直到遍历到一个和slow值不一样的
-
slow和fast指向的结点值不一样,那么在(slow, fast)区间内的结点都是要被删除的,这时候可以更新slow了
slow.next = fast,那么(slow, fast)区间内的结点都是要被抛弃了
slow = fast,那么此时slow指向的结点就是,当前最新去重后的链表尾部,slow及其前面的结点都是非重复的
最巧妙的是:
最后一步:slow.next = null
,遍历结束之后,将slow后面的重复结点都删除
eg:[1,1,2,3,3] while循环结束时,slow指向第一个3的结点(表示该结点及其之前都没有重复),fast指向null,所以说明(slow,fast)都是重复的,所以直接扫尾了slow.next = null;
同样的[1,1,1],也被完美解决了
class Solution {
public ListNode deleteDuplicates(ListNode head) {
if(head == null || head.next == null) return head; // 边界条件
ListNode slow = head, fast = head.next;
while(fast != null){
if(slow.val != fast.val){
slow.next = fast;
slow = slow.next;
}
fast = fast.next;
}
slow.next = null;
return head;
}
}
引申的一题:(参考自https://leetcode-cn.com/problems/remove-duplicates-from-sorted-list/solution/yuan-di-qu-chu-de-zhong-fu-yuan-su-yi-wen-miao-s-2/的总结)
26. 删除排序数组中的重复项
相同之处:都是有序的
区别在于,本题是数组,上题是链表,所以如果需要删除结点,数组耗时需O(N),链表需O(1),所以直觉表明每次遇到重复的,就将后面的数组向前移动是不可取的,最大时间复杂度为O(N^2)
所以要考虑,不对数组的结点进行删除,而是将某些值替换掉,用后面不重复的值替换前面重复的值,那如何指向后面不重复的值呢——fast指针,对应想到的是slow指针
再经过一定的引导(比如说,看题解),就能想到用快慢指针解决,思路和上面的一致:
使用双指针是超级简单的方法:
class Solution {
public int removeDuplicates(int[] nums) {
if(nums.length == 0 || nums.length == 1) return nums.length; // 边界情况
int slow = 0, fast = 1;
for(; fast < nums.length; fast++){
if(nums[slow] != nums[fast]){
nums[++slow] = nums[fast];
}
}
return slow + 1;
}
}
稍微优化:
class Solution {
public int removeDuplicates(int[] nums) {
if(nums.length == 0 || nums.length == 1) return nums.length; // 边界情况
int slow = 0, fast = 1;
for(; fast < nums.length; fast++){
if(nums[slow] != nums[fast]){
if(fast - slow > 1){ // 避免即使相邻未出现重复,也重新赋值了
nums[slow + 1] = nums[fast];
}
slow++;
}
}
return slow + 1;
}
}
总结:遇到数组、链表题,思考是否能够使用双指针、快慢指针
尤其对于数组非必要删除的情况,能否使用快慢指针
参考:
- https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/solution/shuang-zhi-zhen-shan-chu-zhong-fu-xiang-dai-you-hu/
- https://leetcode-cn.com/problems/remove-duplicates-from-sorted-list/solution/yuan-di-qu-chu-de-zhong-fu-yuan-su-yi-wen-miao-s-2/