题意
删除链表中等于给定值 val 的所有节点。
示例:
输入: 1->2->6->3->4->5->6, val = 6
输出: 1->2->3->4->5
题解一:迭代方法
链表中的删除操作相信大家都会,就是让待删除元素的上一个节点的next指向待删除节点的下一个节点。因此我们需要准备两个指针cur和pre分别指向当前节点与上一个节点。此外由于待删除元素的位置可以任意,对链表的处理也有几种情况,如下。
示例1:目标节点在中间
1->2->6->3->4->5->6->null, val = 6
1.1这是一种需要删除的节点在中间的情况,如上所示,那删除操作就很简单,让6的上一个节点2指向其下一个节点3,结果如下。
1->2->3->4->5->6
1.2对于链表中剩余的一个节点6也是一样,让前面的节点5指向6的下一个节点null就完成了任务。
1->2->3->4->5->null
示例2:目标节点出现在一开始
6->6->6->1->2->6->3->4->5->6->null, val = 6
2.这时候头节点也是要删除的节点,所以找不到之前的节点来指向下一个节点,这时候我们可以采用两种方法来处理:
(1)对头节点进行向后的迭代,直到头节点不是目标节点,这也是常规的迭代方法的思路。迭代后的结果如下。
1->2->6->3->4->5->6->null
(2)使用虚拟头节点,即在现在的头节点6前增加一个头节点,这样就可以做到将所有节点的处理变得统一,代码也更加简洁。增加头节点示例如下。
newNode->6->6->6->1->2->6->3->4->5->6->null
1.1常规迭代方法
在上面提到,常规迭代方法通过对头节点进行迭代更新,从而找到一个非目标节点的节点作为新的头节点,然后再对之后的节点进行处理。在此使用while循环更新头节点,如下所示。
//更新头节点,防止头节点值为val
while(head!=null&&head.val==val)
head=head.next;
处理完头节点后有两种可能:(1)头节点非空且不是目标节点,开始后续操作。(2)头节点为空。在此如果头节点为空说明整个链表都是目标节点,我们直接返回null即可。整体代码如下。
class Solution {
public ListNode removeElements(ListNode head, int val) {
if(head==null)return null;
//更新头节点,防止头节点值为val
while(head!=null&&head.val==val)
head=head.next;
if(head==null)return null;
//此时head不为空且必不等于val
ListNode node=head.next,pre=head;//node表示当前节点
while(node!=null){//遍历所有节点
if(node.val==val)//当前节点为val
pre.next=node.next;
else
pre=node;//更新前一个节点
node=node.next;
}
return head;
}
}

1.2增加头节点的迭代方法
ListNode preHead=new ListNode(1);
preHead.next=head;
ListNode pre=preHead,node=head;
只需增加一个虚拟头节点,后续的操作就变得非常方便了。
/**
*虚拟头节点
*/
class Solution {
public ListNode removeElements(ListNode head, int val) {
ListNode preHead=new ListNode(1);
preHead.next=head;
ListNode pre=preHead,node=head;
while(node!=null){
if(node.val==val)
pre.next=node.next;
else
pre=node;
node=node.next;
}
return preHead.next;
}
}
递归方法
很多的题目都可以使用递归方法,递归的方法看起来很巧妙,而且代码量极少,不过有时候效率不会太高。在此也可以用递归方法来求解这道题。
class Solution {
public ListNode removeElements(ListNode head, int val) {
}
}
先来看题目要求我们完成的方法,一般我在使用递归时,都会给进行递归的方法下一个定义,即明确这个方法需要完成什么功能。在此,removeElements方法会对以head为头的链表进行处理,删除其中所有的目标节点(即元素值为val的节点),然后返回处理后的头节点。下面举个例子。
6->6->1->2->6->3->5->null, val = 6//调用方法前
removeElements(head,6);
1->2->3->5->null//调用方法后
递归方法的精髓就在于调用自身来解决问题,那么既然removeElements可以实现处理一条链表的功能,那么我们就可以在removeElements方法中调用自身来处理当前链表的子链,然后通过处理子链前的单个节点来完成整条链表的处理。

使用removeElements方法处理子链表。

如果子链表处理完了,那么这时我们就该处理子链表前的头节点了。
public ListNode removeElements(ListNode head, int val) {
ListNode subHead=removeElements(head.next, val);//子链头节点
...
}
如果当前头节点不是目标节点,那么让头节点与子链相连,然后返回头节点。否则的话,头节点就不再需要了,我们直接返回子链头节点即可。
public ListNode removeElements(ListNode head, int val) {
ListNode subHead=removeElements(head.next, val);//子链头节点
if(head.val==val)return subHead;
head.next=subHead;
return head;
}
也可以这样写:
public ListNode removeElements(ListNode head, int val) {
head.next = removeElements(head.next, val);
return head.val!=val?head:head.next;
}
最后还要加上边界条件。因为这个处理的逻辑对任何一个节点都适用,所以只需要加上边界条件,方法就完成了。显然不管处理什么样的链表,该方法都会优先处理子链表,进入子链表后还是优先处理子链表,直到链表只有一个节点,它的子链表为null,那么完成对它的子链表的处理,只需要返回空即可,所以补上条件,root为空则返回空就大功告成。
class Solution {
public ListNode removeElements(ListNode head, int val) {
if(head==null)return null;
head.next = removeElements(head.next, val);
return head.val!=val?head:head.next;
}
}
本文详细介绍了如何使用迭代和递归方法删除链表中所有值等于给定值的节点。迭代法包括常规迭代和使用虚拟头节点的迭代,递归法则利用递归处理子链表,简化代码。示例展示了不同情况下的处理策略,并提供了完整的代码实现。
1085

被折叠的 条评论
为什么被折叠?



