203.移除链表元素

92 阅读4分钟

1.力扣题目链接

(opens new window)

题意:删除链表中等于给定值 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 输出:[]

class Solution {
    public ListNode removeElements(ListNode head, int val) {
        if (head == null) {
            return head;
        }

        ListNode dummy = new ListNode(-1, head);
        ListNode pre = dummy;
        ListNode cur = head;

        while (cur != null) {
            if (cur.val == val) {
                pre.next = cur.next;
            } else {
                pre = cur;
            }
            cur = cur.next;
        }

        return dummy.next;
    }
}

在这段代码中,return dummy.next 是为了返回删除指定值节点后的新链表的头节点。让我解释一下为什么这样做是正确的。

这段代码首先创建了一个虚拟节点 dummy,它的值设置为 -1,并且将原始链表的头节点 head 设置为 dummy 的下一个节点,这是为了处理删除头节点的情况。

然后,代码使用 precur 两个指针来遍历链表,其中 pre 始终指向当前节点的前一个节点,而 cur 指向当前节点。如果当前节点的值等于要删除的值 val,那么就将 pre.next 指向 cur.next,从而跳过当前节点,相当于删除了当前节点。如果不等于 val,则将 pre 移动到 cur

最后,当遍历完成后,整个链表中值等于 val 的节点都被删除了,而新链表的头节点仍然是 dummy 的下一个节点,因此返回 dummy.next 就是返回删除指定值节点后的新链表的头节点。

这种做法的好处是不需要特殊处理头节点的删除操作,因为始终使用 dummy 节点作为头节点的前一个节点,这样可以简化代码逻辑。

原始链表: 1 -> 2 -> 6 -> 3 -> 4 -> 5 -> 6

我们希望删除值为 6 的节点。

首先,我们创建一个虚拟节点 dummy,并将原始链表的头节点 head 设置为 dummy 的下一个节点,初始情况下链表如下:

dummy -> 1 -> 2 -> 6 -> 3 -> 4 -> 5 -> 6 ^ head

然后,我们初始化两个指针,precur,都指向 dummy

dummy -> 1 -> 2 -> 6 -> 3 -> 4 -> 5 -> 6 ^ ^ pre cur

接下来,我们开始遍历链表。首先,cur 指向节点 1,发现节点值不等于 6,所以只更新 precur,然后将 cur 移向下一个节点:

dummy -> 1 -> 2 -> 6 -> 3 -> 4 -> 5 -> 6 ^ ^ pre cur

接着,cur 指向节点 2,同样发现节点值不等于 6,继续更新 precur,然后将 cur 移向下一个节点:

dummy -> 1 -> 2 -> 6 -> 3 -> 4 -> 5 -> 6 ^ ^ pre cur

现在,cur 指向节点 6,发现节点值等于 6,所以将 pre.next 指向 cur.next,相当于跳过了节点 6

dummy -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 ^ ^ pre cur

接下来,cur 指向节点 3,继续更新 precur,然后将 cur 移向下一个节点,一直执行这个过程:

dummy -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 ^ ^ pre cur

dummy -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 ^ ^ pre cur

dummy -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 ^ ^ pre cur

最终,cur 移动到链表末尾的 null,循环结束。此时,链表变成了如下所示:

dummy -> 1 -> 2 -> 3 -> 4 -> 5

最后,代码返回 dummy.next,也就是返回新链表的头节点 1。所以,删除值为 6 的节点后,链表变为 1 -> 2 -> 3 -> 4 -> 5,并且返回的头节点为 1

3.根据 ListNode dummy = new ListNode(-1, head); ListNode pre = dummy; ListNode cur = head;当pre移动的时候,dummy不移动吗?

在这段代码中,dummy 是一个指向链表头部的虚拟节点,它的值设定为 -1,主要用来简化链表操作,特别是在处理头节点的情况时。dummy 节点的位置不会移动,它一直作为链表头节点的前一个节点存在。

在代码中,precur 分别是两个指针,用于遍历链表。pre 用来指向当前节点的前一个节点,而 cur 指向当前节点。

当需要删除一个节点时,pre 的作用是更新它的 next 指针,以跳过当前节点,使得链表连接起来的时候可以实现节点的删除。但是,dummy 节点仍然保持不动,始终位于链表的开头,不会移动。

dummy 节点的目的是让我们能够处理链表头部的节点和非头部的节点都使用相同的逻辑,而不需要特殊处理头节点的删除操作。当遍历链表时,precur 在移动,而 dummy 保持在链表的开头,起到辅助作用,使得操作更加简洁和统一。