LeetCode 203. Remove Linked List Elements

题目

Remove all elements from a linked list of integers that have value val.

Example:

Input:  1->2->6->3->4->5->6, val = 6
Output: 1->2->3->4->5

2022.12.18

三年过去了,还是栽在同样的地方:漏掉了连续出现的情况。这次自己写直接用了dummyHead,记录了prev和curr,如果curr.val == val那就让prev.next = curr.next,然后if外面挪prev和curr,结果就是[7, 7, 7, 7], val = 7的case过不去。偷看了答案才发现其实只要把prev = curr放到if后面的else就行了,因为只有当不需要remove的时候才会需要移动prev,如果要remove的话,prev还是保持不变。

**
 * 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 removeElements(ListNode head, int val) {
        ListNode dummyHead = new ListNode();
        dummyHead.next = head;
        ListNode curr = head;
        ListNode prev = dummyHead;
        while (curr != null) {
            if (curr.val == val) {
                prev.next = curr.next;
            } else {
                prev = curr;  // this must be put into else, cannot move prev in every case
            }
            curr = curr.next;
        }
        return dummyHead.next;
    }
}

还有一种方法是不需要用prev指针的,就是根据curr.next的值来判断,然后移动curr.next,相当于是curr用作prev。如果curr.next.val == val, 就curr.next = curr.next.next。这里需要注意跟上面的方法一样,都得把移动prev(在这种写法里是移动curr)放在else里,不然还是无法处理连续出现的情况。又被坑了一下。

/**
 * 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 removeElements(ListNode head, int val) {
        if (head == null) {
            return null;
        }
        ListNode dummyHead = new ListNode();
        dummyHead.next = head;
        ListNode curr = dummyHead;
        while (curr.next != null) {
            if (curr.next.val == val) {
                curr.next = curr.next.next;
            } else {
                curr = curr.next;  // this must be put into the else block
            }
        }
        return dummyHead.next;
    }
}

然后是递归的解法,有了上一题( LeetCode 83. Remove Duplicates from Sorted List_wenyq7的博客-CSDN博客)的递归经验,这题就好写很多了。但是也有点confused是否应该把head.val == val放在递归终止条件里。想到后面要根据head.val来判断就没放了。事实证明是正确的。

/**
 * 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 removeElements(ListNode head, int val) {
        if (head == null) {
            return null;
        }
        head.next = removeElements(head.next, val);
        if (head.val == val) {
            return head.next;
        } else {
            return head;
        }
    }
}

这道题是要删除链表里给定数字对应的节点,注意到这个数字可能出现在这个链表的head、tail以及任何地方,也可以连续或者隔着出现几次,因此需要仔细地分情况讨论。用最最普通的做法花了不到十分钟,刚开始一不小心漏了连续出现的情况,后来很快自己发现了问题就accpeted了,实在是太感动辽!

我的想法就非常普通,首先是特殊情况的判断,如果head为null那就返回null(后来一想好像我这个代码不需要额外判断,因为后面会直接返回head……),如果在head出现要删除的数字,那就把它删到head不是指定数字,接下来就可以正常操作了。

因为已经确保了head不需要删除,所以就直接判断curr->next是否需要被删。这里就是刚开始被坑的地方,我直接用了if语句,没有考虑到连续出现的情况,导致在[1, 2, 2, 1]的test case只删了一个2,因为把第一个2删了以后,我的curr直接指向了第二个2,而我已经充分“相信”了curr,导致没有判断它。最后只要把if改成while就可以了,如果下一个还是要被删,那就删到它不是带删的数字为止。

代码如下,时间复杂度O(n),24ms,94.6%,空间复杂度O(1),10.9M,79.25%:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        if (!head) {
            return head;
        }
        while (head && head->val == val) {
            head = head->next;
        }
        ListNode* curr = head;
        while (curr) {
            while (curr->next && curr->next->val == val) {
                curr->next = curr->next->next;
            }
            curr = curr->next;
        }
        return head;
    }
};

看discussion的时候看到有另外一种处理head为待删节点的方法,就是先不处理,直接从head->next开始判断,最后return的时候返回这么一句话:return head->val == val ? head->next : head,感觉真是非常巧妙。因为head->next已经被判断过了,所以最后return head->next是很安全的。

另外,还看到一种写法跟我差不多,但是是我最开始写错的那种if的写法,其实只要把curr = curr->next这句放在else里面就不会出现我之前的错误,因为curr就不会被移动。(LeetCode - The World's Leading Online Programming Learning Platform

还有一种不需要用while处理头节点的方法,就是加一个pseudo head,先new ListNode* pseudo_head = new ListNode(0),后面就还是一样的写法就好啦,只是最后注意一下是return pseudo_head->next。(这篇python的文章讲得很好:LeetCode - The World's Leading Online Programming Learning Platform

另外还有递归的解法,我觉得不如迭代这么直接,而且还会占用过多空间,贴个链接在这里就先不研究了节约时间:3 line recursive solution - Remove Linked List Elements - LeetCode

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值