[链表]82.83. 删除排序链表中的重复元素 I II(三指针法)

82. 删除排序链表中的重复元素 II(删除所有重复元素)

题目链接:82. 删除排序链表中的重复元素 II

分类:链表、三指针法

在这里插入图片描述

思路:三指针法

算法设计:

设置一个pre指向head的前一个节点,last作为不重复链表的最后一个节点,head作为工作指针,dummy作为虚拟头结点。

把不重复链表设为新链表,和删除排序数组重复项的解题思路类似,last用于指示构造的新链表的末尾,每有一个满足条件的节点,就加入到last.next上。

初始时:

dummy->1->2->3->3->4->4->5
       ↑  ↑  ↑
     last pre head

记录pre.val为backup,然后,比较当前head.val和backup:

  • 如果head.val != backup,说明head和pre是不同数字,pre可以加入新链表,令last.next = pre,更新last = last.next,pre和head同步后移一位(pre = head、head = head.next),更新backup = pre.val;
  • 如果head.val == backup,说明head和pre是同个数字,pre不能加入新链表,last保持不动,head不断后移(用while循环实现),直到head.val != backup,或head == null,更新pre和head(pre = head、head = head.next),更新backup = pre.val。(关键点,想到这一点问题基本解决了

重复上述步骤,直到pre==null。(注意:加入新链表的是pre)

算法流程:

举例:

dummy->1->2->3->3->3->4->4->5
  ↑    ↑  ↑
 last pre head

backup = pre.val = 1,head.val != backup,所以将pre加入last(last.next=pre),更新last = last.next,然后pre、head同步后移,更新backup为新pre.val.

dummy->1->2->3->3->3->4->4->5
       ↑  ↑  ↑
    last pre head

backup = 2,head.val != backup,所以将pre加入last(last.next=pre),更新last = last.next,pre、head同步后移,更新backup。

dummy->1->2->3->3->3->4->4->5
          ↑  ↑  ↑
       last pre head

backup = 3,head.val == backup,所以last保持不动,head进入while循环不断取head.next,直到head.val != backup,更新pre = head,head = head.next,更新backup = pre.val:

dummy->1->2->3->3->3->4->5
          ↑           ↑  ↑
        last         pre head

backup = 4,head.val != backup ,所以将pre加入last(last.next=pre),更新last = last.next,pre、head同步后移,更新backup = pre.val = 5:

dummy->1->2->3->3->3->4->5->null
          ↑              ↑  ↑
        last            pre head

可以发现此时head=null,但pre还未加入新链表中,所以head == null不能作为退出循环的条件。此时head==null,所以无法取到head.val,但此时的pre可以直接加入新链表:last.next = pre,然后更新pre = head = null,至此退出循环。(关键点1:边界条件的考虑

最后将pre = null添加到last.next上,作为新链表的结尾。(关键点2:pre=null时的收尾处理

实现时遇到的问题:

1、边界条件的考虑

通过上面的举例分析,可以发现当head=null时,pre还未加入新链表中,所以head == null不能作为退出循环的条件。又因为此时head==null,所以无法取到head.val,但此时的pre不可能和后面的其他节点重复,所以可以直接加入新链表:last.next = pre,然后更新pre = head = null,至此退出循环。

2、pre=null时的收尾处理(易遗漏)

例如:用例[1,1],直到pre == null退出循环时last也没有新元素加入,这样一来last.next仍然是初始状态,指向原链表,所以在退出循环后增加一步:

last.next = pre

使 last.next = null, 最终返回[]。

实现代码:

class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        //特殊用例
        if(head == null || head.next == null) return head;

        ListNode dummy = new ListNode(0);//虚拟头结点
        dummy.next = head;
        //last指向新链表的末尾,pre指向head的前一个节点
        ListNode last = dummy, pre = head;
        head = head.next;

        //记录下pre的val与head.val比较
        int backup = pre.val;
        while(pre != null){
            //当head.val!=backup或head==null时
            if(head == null || head.val != backup){
                last.next = pre;
                last = last.next;
            }
            //当head.val==backup时
            else{
                //head不断取head.next,直到head.val!=backup
                while(head != null && head.val == backup){
                    head = head.next;
                }
            }
            //更新pre,head
            pre = head; 
            if(head != null) head = head.next;
            //更新backup
            if(pre != null) backup = pre.val;
        }
        last.next = pre;//易忽略点:见实现遇到的问题
        return dummy.next;
    }
}

83. 删除排序链表中的重复元素 I(重复元素保留一个)

题目链接:83. 删除排序链表中的重复元素 I

分类:链表、三指针法

在这里插入图片描述

题目分析

和82题相比,本题更简单,对重复的元素只保留一个,但思路类似,仍然使用三指针法,但不需要使用变量backup记录pre.val,主要工作内容是:

pre指向head的前一个节点,两个指针始终同步移动,当head.val!=pre.val,说明head,pre是值不同的节点,将pre加入新链表。无论head.val和pre.val的大小关系如何,都将pre和head同步向后移动,直到pre == null退出。

最后也不需要额外将pre == null加入新链表末尾(举例分析可知,加入也不影响)。

思路:三指针法

设dummy为虚拟头结点,last指示新链表的最后一个节点,head作为工作节点,pre执行head的前一个节点。
初始时:新链表为空,所以last指向dummy

dummy->1->1->1->2->2->3
  ↑    ↑  ↑
 last pre head
  • 如果pre.val == head.val,说明head和pre是重复元素,pre,head继续不断同步向后移动,直到head.val != pre.val或head == null;
  • 如果pre.val != head.val,说明head和pre是不同元素,将pre加入新链表(last.next = pre),同步向后移动pre和head(pre = head,head = head.next)。

以此类推,注意当head == null时,pre还未加入新链表,所以pre加入新链表,再更新pre = pre.next == null,退出while循环,
如下:

dummy->1->1->1->2->2->3->null
  ↑                   ↑   ↑
 last                pre head

实现代码:

class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        //特殊用例
        if(head == null || head.next == null) return head;
        //虚拟头结点
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        //设置各指针的初始状态
        ListNode last = dummy, pre = head;
        head = head.next;
        while(pre != null){
            if(head == null || pre.val != head.val){
                last.next = pre;
                last = last.next;
            }
            pre = head;
            if(head != null) head = head.next;
        }
        return dummy.next;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值