203.移除鏈表元素
這是LeetCode第203號問題,題目鏈接:https://leetcode-cn.com/problems/remove-linked-list-elements/
刪除鏈表中等於給定值 val 的所有節點。
示例:
輸入: 1->2->6->3->4->5->6, val = 6
輸出: 1->2->3->4->5
ListNode.java
LeetCode中提供的節點,爲了本地測試方便,添加方法進行完善。
public class ListNode {
int val;
ListNode next;
ListNode(int x) {
val = x;
}
public ListNode(int[] arr) {
if (arr == null || arr.length == 0)
throw new IllegalArgumentException("arr can not be empty");
// val 等於第一個元素
this.val = arr[0];
// 把數組中元素創建成一個個新的ListNode接在前一個節點上
ListNode cur = this;
for (int i = 1; i < arr.length; i++) {
cur.next = new ListNode(arr[i]);
cur = cur.next; // 挪位置
}
// 之後this就是用循環創建的鏈表的頭節點
}
// 返回以當前節點爲頭節點的字符串
@Override
public String toString() {
StringBuilder res = new StringBuilder();
ListNode cur = this;
while (cur != null) {
res.append(cur.val + "-->");
cur = cur.next;
}
res.append("NULL");
return res.toString();
}
}
不使用虛擬頭節點
按照思路一步一步實現
// 不創建虛擬頭節點
class Solution {
public ListNode removeElements(ListNode head, int val) {
// 如果頭節點是待刪除元素,需要刪除
// 使用一個循環刪除,存在頭節點後一個元素也是需要刪除的
while (head != null && head.val == val) { // 頭節點不能爲空
ListNode delNode = head;
head = head.next;
delNode.next = null; // 斷開鏈接
}
if (head == null) {
return null;
}
// 從中間刪,此時head一定不是待刪除元素,head後一個節點可能是帶刪除元素
ListNode prev = head; // prev爲待刪除元素前一個元素
while (prev.next != null) {
if (prev.next.val == val) {
ListNode delNode = prev.next;
prev.next = delNode.next;
delNode.next = null;
} else {
// 否則的話不用刪除,往回偏移
prev = prev.next;
}
}
return head;
}
}
上述代碼簡寫
class Solution {
public ListNode removeElements(ListNode head, int val) {
// 如果頭節點是待刪除元素,需要刪除
// 使用一個循環刪除,存在頭節點後一個元素也是需要刪除的
while (head != null && head.val == val) { // 頭節點不能爲空
head = head.next;
}
if (head == null) {
return null;
}
// 從中間刪,此時head一定不是待刪除元素,head後一個節點可能是帶刪除元素
ListNode prev = head; // prev爲待刪除元素前一個元素
while (prev.next != null) {
if (prev.next.val == val) {
prev.next = prev.next.next;
} else {
// 否則的話不用刪除,往回偏移
prev = prev.next;
}
}
return head;
}
使用虛擬頭節點
// 使用虛擬頭節點
class Solution {
public ListNode removeElements(ListNode head, int val) {
ListNode dummyHead = new ListNode(-1); // 隨便給一個,永遠不會訪問到
dummyHead.next = head; // 不需要對第一個節點進行特殊處理
ListNode prev = dummyHead;
while (prev.next != null) {
if (prev.next.val == val)
prev.next = prev.next.next;
else
prev = prev.next;
}
return dummyHead.next;
}
}
鏈表具有遞歸性質
把一個一個節點連接起來就是一個鏈表
對於鏈表0->1->2->3->4->5->NULL
,可以想像成0->和一個更短的鏈表(少了一個節點)
,鏈表可以理解爲一個一個節點的掛接,也可以理解成一個頭節點後面掛接了一個更小的鏈表。
- 在這個更短的鏈表中,“1”就變成了它的頭節點。
- 更短的鏈表也可以類似的看作
頭節點“1”掛接了一個比現在更小的鏈表
,在這個更小的鏈表中,“2”作爲它的頭節點。 - 依此類推,直到最後,可以理解
NULL本身也是一個鏈表
,它也是最基本的鏈表。
使用遞歸刪除鏈表中的元素
class Solution {
public ListNode removeElements(ListNode head, int val) {
// 對於空鏈表刪除所有元素爲val的節點,得到的還是空
if (head == null)
return null;
// 將頭節點後的鏈表中所有的val值刪除後剩下的鏈表
ListNode res = removeElements(head.next, val);
// 判斷頭節點的值是否爲val
if (head.val == val)
return res;
// head的值不爲val時候返回head和後面的鏈表
else {
// 讓head掛接上後面的鏈表
head.next = res;
return head;
}
}
}
代碼簡寫
// 使用遞歸來解決
class Solution5 {
public ListNode removeElements(ListNode head, int val) {
// 對於空鏈表刪除所有元素爲val的節點,得到的還是空
if (head == null)
return null;
// 將頭節點後的鏈表中所有的val值刪除後剩下的鏈表
head.next = removeElements(head.next, val);
// 三目運算符簡寫
return (head.val == val) ? head.next : head;
}
}
書寫測試主函數
public static void main(String[] args) {
int[] arr = {1, 2, 6, 3, 4, 5, 6};
ListNode list = new ListNode(arr);
System.out.println(list);
ListNode res = (new Solution()).removeElements(list, 6);
System.out.println(res);
}
測試結果
1-->2-->6-->3-->4-->5-->6-->NULL
1-->2-->3-->4-->5-->NULL