文章目录
题目
标题和出处
标题:移除链表元素
出处:203. 移除链表元素
难度
3 级
题目描述
要求
给你一个链表的头结点 head \texttt{head} head 和一个整数 val \texttt{val} val,请你删除链表中所有满足 Node.val = val \texttt{Node.val} = \texttt{val} Node.val=val 的结点,并返回新的头结点。
示例
示例 1:
输入:
head
=
[1,2,6,3,4,5,6],
val
=
6
\texttt{head = [1,2,6,3,4,5,6], val = 6}
head = [1,2,6,3,4,5,6], val = 6
输出:
[1,2,3,4,5]
\texttt{[1,2,3,4,5]}
[1,2,3,4,5]
示例 2:
输入:
head
=
[],
val
=
1
\texttt{head = [], val = 1}
head = [], val = 1
输出:
[]
\texttt{[]}
[]
示例 3:
输入:
head
=
[7,7,7,7],
val
=
7
\texttt{head = [7,7,7,7], val = 7}
head = [7,7,7,7], val = 7
输出:
[]
\texttt{[]}
[]
数据范围
- 列表中的结点数目在范围 [0, 10 4 ] \texttt{[0, 10}^\texttt{4}\texttt{]} [0, 104] 内
- 1 ≤ Node.val ≤ 50 \texttt{1} \le \texttt{Node.val} \le \texttt{50} 1≤Node.val≤50
- 0 ≤ val ≤ 50 \texttt{0} \le \texttt{val} \le \texttt{50} 0≤val≤50
解法一
思路和算法
根据链表的特点,可以使用递归的做法移除链表元素。递归做法可以分成两步:
-
对除了头结点以外的结点进行移除链表元素操作,删除结点值等于 val \textit{val} val 的全部结点;
-
判断头结点的值是否等于 val \textit{val} val,决定是否要删除头结点。
递归的终止条件是空链表。当链表为空时,不包含任何结点,因此直接返回空链表。
当链表非空时,递归地移除链表元素,然后根据头结点的值是否等于 val \textit{val} val 决定是否要删除头结点。
代码
class Solution {
public ListNode removeElements(ListNode head, int val) {
if (head == null) {
return head;
}
head.next = removeElements(head.next, val);
return head.val == val ? head.next : head;
}
}
复杂度分析
-
时间复杂度: O ( n ) O(n) O(n),其中 n n n 是链表的长度。需要遍历链表一次,对于每个要删除的结点,删除一个结点的时间是 O ( 1 ) O(1) O(1)。
-
空间复杂度: O ( n ) O(n) O(n),其中 n n n 是链表的长度。空间复杂度主要取决于递归调用栈的深度,递归调用栈最多不会超过 n n n 层。
解法二
思路和算法
删除一个结点的做法是,定位到待删除结点的前一个结点,将前一个结点的 next \textit{next} next 指针指向待删除结点的后一个结点。假设待删除的结点是 curr \textit{curr} curr,该结点的前一个结点是 prev \textit{prev} prev,则在删除结点之前有 prev . next = curr \textit{prev}.\textit{next} = \textit{curr} prev.next=curr,删除结点时,将 prev . next \textit{prev}.\textit{next} prev.next 指向 curr . next \textit{curr}.\textit{next} curr.next,即完成了将结点 curr \textit{curr} curr 删除的操作。
由于头结点可能是待删除的结点,因此创建哑结点 dummyHead \textit{dummyHead} dummyHead,令 dummyHead . next = head \textit{dummyHead}.\textit{next} = \textit{head} dummyHead.next=head。从 dummyHead \textit{dummyHead} dummyHead 开始遍历链表,用 temp \textit{temp} temp 表示遍历到的结点,进行如下操作:
-
如果 temp . next . val \textit{temp}.\textit{next}.\textit{val} temp.next.val 等于 val \textit{val} val,则将 temp . next \textit{temp}.\textit{next} temp.next 指向 temp . next . next \textit{temp}.\textit{next}.\textit{next} temp.next.next;
-
否则, temp . next \textit{temp}.\textit{next} temp.next 不删除,将 temp \textit{temp} temp 向后移动一步。
需要注意的是,当 temp . next \textit{temp}.\textit{next} temp.next 需要删除时,删除结点之后,由于新的 temp . next \textit{temp}.\textit{next} temp.next 可能也要删除,因此不能将 temp \textit{temp} temp 向后移动。
遍历到链表末尾时,遍历结束。新链表的头结点为 dummyHead . next \textit{dummyHead}.\textit{next} dummyHead.next。
代码
class Solution {
public ListNode removeElements(ListNode head, int val) {
ListNode dummyHead = new ListNode(0);
dummyHead.next = head;
ListNode temp = dummyHead;
while (temp.next != null) {
if (temp.next.val == val) {
temp.next = temp.next.next;
} else {
temp = temp.next;
}
}
return dummyHead.next;
}
}
复杂度分析
-
时间复杂度: O ( n ) O(n) O(n),其中 n n n 是链表的长度。需要遍历链表一次,对于每个要删除的结点,删除一个结点的时间是 O ( 1 ) O(1) O(1)。
-
空间复杂度: O ( 1 ) O(1) O(1)。