刪除單鏈表中所有值等於 `val` 的節點。本文推薦使用**虛擬頭節點**統一處理頭節點刪除邊界情況,給出清晰的雙指針遍歷刪除邏輯,並分析常見錯誤與內存管理細節。
若頭節點值等於 `val`,需更新 `head` 指針。常規寫法需單獨處理頭節點,代碼冗餘。
**虛擬頭節點** 技巧:在真實頭節點前創建一個 `dummy` 節點,`dummy->next = head`。統一從 `dummy` 開始遍歷,刪除邏輯一致,最後返回 `dummy->next`。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* removeElements(struct ListNode* head, int val) {
// 1. 創建虛擬頭節點
struct ListNode dummy;
dummy.next = head;
dummy.val = 0; // 值無關緊要
// 2. 雙指針遍歷:pre 指向當前檢查節點的前驅
struct ListNode *pre = &dummy;
struct ListNode *cur = head;
while (cur != NULL) {
if (cur->val == val) {
// 刪除 cur:pre->next 跳過 cur
pre->next = cur->next;
// 可選:free(cur) 釋放內存(題目通常不管理)
cur = pre->next; // cur 後移,pre 不動
} else {
// 正常後移
pre = cur;
cur = cur->next;
}
}
// 3. 返回真實頭節點
return dummy.next;
}
struct ListNode* removeElements(struct ListNode* head, int val) {
if (!head) return NULL;
head->next = removeElements(head->next, val);
return (head->val == val) ? head->next : head;
}
遞歸優雅但大鏈表可能棧溢出,面試首選迭代虛擬頭節點法。
| 輸入鏈表 | val | 輸出 | 說明 |
|---|---|---|---|
| 1→2→6→3→4→5→6 | 6 | 1→2→3→4→5 | 中間、尾部刪除 |
| 7→7→7→7 | 7 | 空 | 全刪除 |
| 空 | 1 | 空 | 邊界 |
| 1→2→3 | 4 | 1→2→3 | 無刪除 |
| 1→1→2→3 | 1 | 2→3 | 連續頭節點刪除 |
移除鏈表元素是鏈表基本操作核心考點。**虛擬頭節點 + 雙指針** 是處理頭節點邊界條件的標準模板,邏輯統一、代碼簡潔、無特殊情況分支。掌握此模板,可直接應用於「刪除倒數第 N 個」、「分隔鏈表」等眾多鏈表題。