83. 删除排序链表中的重复元素 & 26. 删除排序数组中的重复项(双指针)【S】

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;
    }
}

总结:遇到数组、链表题,思考是否能够使用双指针、快慢指针
尤其对于数组非必要删除的情况,能否使用快慢指针
参考:

  1. https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/solution/shuang-zhi-zhen-shan-chu-zhong-fu-xiang-dai-you-hu/
  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/
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值