题目:
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