JZ76 删除链表中重复的结点

JZ76 删除链表中重复的结点

描述

在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5。

示例1

输入:

{1,2,3,3,4,4,5}

返回值:

{1,2,5}

示例2

输入:

{1,1,1,8}

返回值:

{8}

解析

本题的要求很简单,就是要删除链表中出现了两次以上的节点(全部删除,不是删除多余的)。对于这个问题,有两种思路为使用辅助空间和不使用辅助空间。

不使用辅助空间

因为链表是有序的,所以我们可以不使用辅助空间,即只在原链表上进行操作而实现目标。此时我们通过 pre 指针和 cur 指针对链表进行遍历:

  1. 先设置 pre 指针比 cur 指针慢一步;

  2. 对 cur 指针走到的节点进行判断,若当前节点( cur )与下一个节点( cur.next )的值相同,说明有重复节点;

  3. 则 pre 指针留在原地,cur 指针持续往前移动,直到 cur 与 cur.next 的值不再相同为止,也就是说将 cur 从重复段的开头移动到了结尾;

  4. 此时 cur 位于重复段结尾,故将位于重复段开头前的 pre 指针的 next 设置为位于重复段结尾的 cur 的下一个节点( cur.next ),就相当于删除了重复段。

以上就是解决问题的基本步骤,但在实现时遇到了一个问题:pre 指针需要比 cur 指针慢一步,在初始化时,是不是要将 pre 设置为第一个节点,cur 设置为第二个节点呢?这是行不通的,因为会导致第一个节点和第二个节点重复时无法删除。

要解决这个问题,需要一个比较巧妙的方法:手动添加一个伪头节点 head,其后继是链表的头节点 pHead,同时保证 head 的值不会与 pHead 的值相同。有了这个伪头节点,cur 就可以设置为 pHead,pre 就设置为 head,这样 cur 就可以从链表头节点开始遍历了!在返回时,只需要返回 head.next 就相当于删除掉这个伪头节点了!

代码清单

/*
 public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    public ListNode deleteDuplication(ListNode pHead) {
        if(pHead == null)
            return null;
        ListNode head = new ListNode(Integer.MAX_VALUE);
        head.next = pHead;
        ListNode pre = head;
        ListNode cur = head.next;
        while( cur != null ){
            if( cur.next != null && cur.val == cur.next.val){
                while( cur.next != null && cur.val == cur.next.val)
                    cur = cur.next;
                cur = cur.next;
                pre.next = cur;
            }else{
                pre = pre.next;
                cur = cur.next;
            }
        }
        return head.next;
    }
}
使用辅助空间

因为问题涉及到重复元素,所以也可以直接使用 Set 集合。先进行一次遍历将重复的元素存入 Set 中(遍历时当前元素和下一个元素相同就存入,由于 Set 的性质即使多次重复也只会存入一次),再进行二次遍历检验每个元素是否存在于 Set 中,若存在则说明是重复元素,需要调整指针关系删除当前元素。

与不使用辅助空间相同,因为头结点具有特殊性,所以也需要创建一个伪头结点,具体内容同上。

代码清单

import java.util.Set;
import java.util.HashSet;
/*
 public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    public ListNode deleteDuplication(ListNode pHead) {
        if(pHead == null)
            return null;
        Set<Integer> nodeSet = new HashSet<>();
        ListNode p1 = pHead;
        while(p1.next != null){
            if(p1.val == p1.next.val)
                nodeSet.add(p1.val);
            p1 = p1.next;
        }
        ListNode head = new ListNode(Integer.MAX_VALUE);
        ListNode p2 = pHead;
        head.next = p2;
        ListNode pre = head;
        ListNode cur = p2;
        while(cur != null){
            if(nodeSet.contains(cur.val)){
                cur = cur.next;
                pre.next = cur;
                continue;
            }
            cur = cur.next;
            pre = pre.next;
        }
        return head.next;
    }
}

总结

最后一道链表的问题了,对于链表相关的题目,基本都需要调整指针关系,所以多设置几个指针的思路会经常用到。

摸了好久,看情况决定后面写什么吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值