删除链表元素专题

本文综合讲述几道删除链表元素的题目

一、LeetCode203.移除链表元素

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。

示例 1:

输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]

示例 2:

输入:head = [], val = 1
输出:[]

示例 3:

输入:head = [7,7,7,7], val = 7
输出:[]

代码如下

lass Solution {
    public ListNode removeElements(ListNode head, int val) {
        // 创建一个虚拟头节点,并将其指向原始链表的头节点
        ListNode dummyHead = new ListNode(0);
        ListNode curr = head; // 当前节点指针,用于遍历链表
        ListNode prev = dummyHead; // 前一个节点指针,用于构建新链表
        dummyHead.next = head; // 虚拟头节点指向原始链表的头节点

        // 遍历链表
        while (curr != null) {
            // 如果当前节点的值等于目标值
            if (curr.val == val) {
                // 将前一个节点的 next 指向当前节点的下一个节点,即删除当前节点
                prev.next = curr.next;
                // 移动当前节点指针到下一个节点
                curr = prev.next;
            } else {
                // 如果当前节点的值不等于目标值
                // 更新前一个节点指针为当前节点
                prev = curr;
                // 移动当前节点指针到下一个节点
                curr = curr.next;
            }
        }

        
        return dummyHead.next;
    }
}

值得注意的点:

1.可能会有疑问,最后直接return head难道不是更加简介吗,何必用dummyhead.next多此一举

这是因为在原始链表的头部可能存在一个值等于 val 的节点,而删除这个节点后,新链表的头部会发生变化。

如果直接返回 head,则可能返回的是一个值等于 val 的节点,而不是删除后的新链表的头节点。

2.ListNode dummyHead = new ListNode(0);

虚拟头结点的用法非常多,也很重要,需要多留意一下

二、LeetCode19.删除链表倒数第N个节点

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

示例 1:

输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]

示例 2:

输入:head = [1], n = 1
输出:[]

示例 3:

输入:head = [1,2], n = 1
输出:[1]

方法一:双指针

代码如下

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode fast = head, slow = head;
        for (int i = 0; i < n; i++){
            fast = fast.next;
        }
        if (fast == null) {
            return head.next; // 删除的是头节点,直接返回头节点的下一个节点
        }
        while (fast.next != null){
            fast = fast.next;
            slow = slow.next;
        }
        slow.next = slow.next.next;
        return head;

    }
}

前一期有关双指针的总结,涉及到倒数第N个元素的时候就可以考虑用它

在提交时,我没有写

 if (fast == null) {
            return head.next; // 删除的是头节点,直接返回头节点的下一个节点
        }

导致报错

这段代码的主要原因是如果要删除的节点恰好是链表的头节点,也就是说 n 等于链表的长度时,代码会出现问题。

在这种情况下,fast 指针移动 n 步后将指向 null,而在代码的最后一行中,会尝试访问 slow.next,而此时 slow 是指向头节点的,它的下一个节点为 null,因此会出现空指针异常。

为了修复这个问题,我们可以添加一个判断条件来检查 fast 是否为 null,如果是,则说明要删除的节点是头节点,直接返回头节点的下一个节点即可。

方法二:计算链表长度

思路:先遍历一遍链表,找到链表总长度L,然后重新遍历,位置L-N+1的元素就是我们要删的。

这里我就不写代码了,显然是用双指针更吊一点嘛,只要遍历一遍,而且更有逼格。

三、LeetCode83.删除排序链表中的重复元素 I

重复元素保留一个

给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。

示例 1:

输入:head = [1,1,2]
输出:[1,2]

示例 2:

输入:head = [1,1,2,3,3]
输出:[1,2,3]

代码如下

class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        if (head == null) { // 如果头节点为空,直接返回空
            return head;
        }
        ListNode cur = head; // 当前节点指针,初始化为头节点
        while (cur.next != null) { // 遍历链表,直到当前节点的下一个节点为空(链表结尾)
            if (cur.val == cur.next.val) { // 如果当前节点的值等于下一个节点的值,说明出现了重复的节点
                cur.next = cur.next.next; // 将当前节点的 next 指针指向下一个节点的下一个节点,跳过重复节点
            } else {
                cur = cur.next; // 如果当前节点的值不等于下一个节点的值,将当前节点指针向后移动一位
            }
        }
        return head; // 返回原始链表的头节点(可能经过删除操作后已经改变)
    }
}

思路很简单:cur 和 curr 相比较,相同就删除,不同就 cur = cur.next

四、LeetCode83.删除排序链表中的重复元素 II

重复元素都不要

给定一个已排序的链表的头 head , 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表 。

示例 1:

输入:head = [1,2,3,3,4,4,5]
输出:[1,2,5]

示例 2:

输入:head = [1,1,1,2,3]
输出:[2,3]

代码如下

class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        if (head == null) { // 如果头节点为空,直接返回空
            return head;
        }
        
        ListNode dummy = new ListNode(0, head); // 创建一个虚拟节点,并将其 next 指针指向头节点
        ListNode cur = dummy; // 当前节点指针,初始化为虚拟节点
        
        while (cur.next != null && cur.next.next != null) { // 遍历链表,直到当前节点的下一个节点或下下个节点为空(链表结尾)
            if (cur.next.val == cur.next.next.val) { // 如果当前节点的 next 节点的值等于下下个节点的值,说明出现了重复的节点
                int x = cur.next.val; // 记录重复节点的值
                while (cur.next != null && cur.next.val == x) { // 循环遍历直到找到不等于重复值的节点
                    cur.next = cur.next.next; // 将当前节点的 next 指针指向下一个节点的下一个节点,跳过重复节点
                }
            } else {
                cur = cur.next; // 如果当前节点的 next 节点的值不等于下下个节点的值,将当前节点指针向后移动一位
            }
        }
        
        return dummy.next; // 返回虚拟节点的下一个节点作为新的头节点(可能经过删除操作后已经改变)
    }
}

这段代码实现了删除链表中重复出现的节点。通过比较当前节点的 next 节点和下下个节点的值。

如果值相等,则将当前节点的 next 指针指向下一个节点的下一个节点,跳过重复节点;

如果值不相等,则将当前节点指针向后移动一位。最后,返回虚拟节点的下一个节点作为新的头节点。

这道题对比前面几道就稍微有点难度了,要注意的细节很多,我是基本没思路的,看来答案的代码理解意思后,自己敲了一遍,结果还有几处不会,又看,又敲。确实值得多花时间的一道题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值