前言:這是專門針對java語言講解的算法解析(題目順序大致參考《代碼隨想錄》)
思維導圖
操作鏈表
刪除節點
刪除鏈表中 D 節點時,只需將其前驅節點 C 的 next 指針指向 D 的下一個節點 E。
添加節點
?
先讓?新節點 F 的 next 指針?指向?C 原來的后繼節點 D(保存原有連接,避免鏈表斷裂);
再讓?C 節點的 next 指針?指向?新節點 F,完成插入。
鏈表基礎代碼
public class ListNode {// 結點的值int val;// 下一個結點ListNode next;// 節點的構造函數(無參)public ListNode() {}// 節點的構造函數(有一個參數)public ListNode(int val) {this.val = val;}// 節點的構造函數(有兩個參數)public ListNode(int val, ListNode next) {this.val = val;this.next = next;}
}
LeetCode203. 移除鏈表元素
?
一、核心邏輯:通過虛擬頭節點統一移除邏輯
鏈表中每個節點包含值和指向下一節點的引用,要移除值為目標值的節點,可引入虛擬頭節點(不存儲實際數據),使其作為原頭節點的前驅。這樣無論是頭節點還是中間節點,都能通過 “前驅節點指針調整” 的方式統一處理,避免頭節點特殊處理的繁瑣。
二、關鍵細節:虛擬頭節點的使用與指針遍歷
移除鏈表元素的難點在于頭節點與其他節點處理邏輯不一致,而虛擬頭節點可消除這種差異。通過維護一個遍歷指針,始終指向當前待檢查節點的前驅,就能統一執行 “跳過目標節點” 的操作。
解法:虛擬頭節點 + 前驅指針遍歷
區間(邏輯范圍)含義:以虛擬頭節點為起點,遍歷指針?prev
?始終指向當前處理節點的前驅,待檢查范圍是?prev.next
?及之后的節點。
循環條件:while (prev.next != null)
因為只要?prev
?的下一個節點存在,就需要檢查其是否為目標值,循環繼續。
指針調整:
- 若?
prev.next.val == val
:說明下一個節點是目標節點,需移除,因此將?prev.next
?指向?prev.next.next
(跳過目標節點)。 - 若?
prev.next.val != val
:說明下一個節點無需移除,prev
?后移一位(prev = prev.next
),繼續檢查后續節點。
返回結果:循環結束后,所有目標節點已被移除,返回虛擬頭節點的下一個節點(即新的頭節點)
代碼示例:
public ListNode removeElements(ListNode head, int val) {while(head!=null && head.val==val) {head = head.next;}ListNode curr = head;while(curr!=null && curr.next !=null) {if(curr.next.val == val){curr.next = curr.next.next;} else {curr = curr.next;}}return head;
}
LeetCode206. 反轉鏈表
如果再定義一個新的鏈表,實現鏈表元素的反轉,其實這是對內存空間的浪費。
其實只需改變鏈表的next指針的指向,直接將鏈表反轉 ,而非重新定義一個新的鏈表,如圖:
我們拿有示例中的鏈表來舉例,如動畫所示:
- 初始化指針:cur 指向頭節點,pre 初始化為 null,tmp 用于臨時保存節點。
- 當 cur 不為 null 時,執行以下操作:
- 保存下一個節點:用 tmp 存儲 cur 的 next 節點(避免后續指向改變后丟失)。
- 反轉當前節點指向:將 cur 的 next 指向 pre。
- 移動指針:pre 移至 cur 位置,cur 移至 tmp 位置(即之前保存的下一個節點)。
- 結果處理:循環結束后,pre 指向反轉后鏈表的新頭節點,返回 pre。
代碼實現:
// 雙指針
class Solution {public ListNode reverseList(ListNode head) {ListNode prev = null;ListNode cur = head;ListNode temp = null;while (cur != null) {temp = cur.next;// 保存下一個節點cur.next = prev;prev = cur;cur = temp;}return prev;}
LeetCode24. 兩兩交換鏈表中的節點
這道題目正常模擬就可以了。
建議使用虛擬頭結點,這樣會方便很多,要不然每次針對頭結點(沒有前一個指針指向頭結點),還要單獨處理。
接下來就是交換相鄰兩個元素了,此時一定要畫圖,不畫圖,操作多個指針很容易亂,而且要操作的先后順序
初始時,cur指向虛擬頭結點,然后進行如下三步:
操作之后,鏈表如下:
看這個可能就更直觀一些了:
代碼實現:
public ListNode swapPairs(ListNode head) {ListNode dummy = new ListNode(0, head);ListNode cur = dummy;while (cur.next != null && cur.next.next != null) {ListNode node1 = cur.next;// 第 1 個節點ListNode node2 = cur.next.next;// 第 2 個節點cur.next = node2; // 步驟 1node1.next = node2.next;// 步驟 3node2.next = node1;// 步驟 2cur = cur.next.next;}return dummy.next;
}
LeetCode19. 刪除倒數第n個節點
雙指針的經典應用,如果要刪除倒數第n個節點,讓fast移動n步,然后讓fast和slow同時移動,直到fast指向鏈表末尾。刪掉slow所指向的節點就可以了。
分為如下幾步:
定義fast指針和slow指針,初始值為虛擬頭結點,如圖:
fast首先走n + 1步 ,為什么是n+1呢,因為只有這樣同時移動的時候slow才能指向刪除節點的上一個節點(方便做刪除操作),如圖:?
fast和slow同時移動,直到fast指向末尾,如題:?
刪除slow指向的下一個節點,如圖:?
代碼實現:
class Solution {public ListNode removeNthFromEnd(ListNode head, int n) {//新建一個虛擬頭節點指向headListNode dummyNode = new ListNode(0);dummyNode.next = head;//快慢指針指向虛擬頭節點ListNode fastIndex = dummyNode;ListNode slowIndex = dummyNode;// 只要快慢指針相差 n 個結點即可for (int i = 0; i <= n; i++) {fastIndex = fastIndex.next;}while (fastIndex != null) {fastIndex = fastIndex.next;slowIndex = slowIndex.next;}// 此時 slowIndex 的位置就是待刪除元素的前一個位置。// 具體情況可自己畫一個鏈表長度為 3 的圖來模擬代碼來理解// 檢查 slowIndex.next 是否為 null,以避免空指針異常if (slowIndex.next != null) {slowIndex.next = slowIndex.next.next;}return dummyNode.next;}
}