一、單鏈表逆置
1、法一
思路:
通過兩個輔助指針?
p
和 q,在遍歷鏈表時逐個反轉指針方向。
- p初始化為?
第一個有效節點
,用于遍歷原鏈表;q初始化為?NULL,
用于臨時保存?p
?的下一個節點。plist->next
?被置為?NULL
,表示逆置后的鏈表初始為空(頭節點指向空)。每次迭代中:
q
?保存?p
?的下一個節點(防止斷鏈)。- 將?
p
?的?next
?指向當前逆置鏈表的頭部(plist->next
)。- 更新頭節點的?
next
?指向?p
,使?p
?成為新鏈表的第一個節點。p
?移動到?q
(原鏈表的下一個節點),繼續循環。終止條件
當?p
?為?NULL
?時,說明原鏈表已全部遍歷完畢,逆置完成。關鍵點說明:
頭插法:通過每次將當前節點插入頭節點之后,實現鏈表的逆置。
斷鏈處理:
q
?臨時保存?p->next
,避免修改?p->next
?后丟失后續節點。時間復雜度:O(n),僅需一次遍歷。
空間復雜度:O(1),僅使用固定數量的指針變量。
示例流程:
假設原鏈表為?
1 -> 2 -> 3 -> NULL
,頭節點為?H
:
- 初始:
H -> 1 -> 2 -> 3
,p
?指向?1
。- 第一次循環后:
H -> 1 -> NULL
,p
?指向?2
。- 第二次循環后:
H -> 2 -> 1 -> NULL
,p
?指向?3
。- 第三次循環后:
H -> 3 -> 2 -> 1 -> NULL
,p
?為?NULL
,退出循環。
代碼實現:
//逆置單鏈表
void Reverse_list(Node* plist) {assert(plist != NULL);assert(plist->next != NULL);assert(plist->next->next != NULL);Node* p = plist->next;Node* q = NULL;plist->next = NULL;while (p != NULL) {q = p->next;p->next = plist->next;plist->next = p;p = q;}
}
2、法二
思路:
指針初始化
p
初始化為第一個有效節點(plist->next
),q
初始化為第二個節點(p->next
)。p->next
被置為NULL
,表示反轉后的新鏈表的尾節點。迭代反轉
r
保存q
的下一個節點(q->next
),防止斷鏈。- 將
q->next
指向p
,完成局部反轉。- 指針
p
和q
分別向后移動一位,繼續處理后續節點。- 循環終止時,
p
指向原鏈表的最后一個節點,即反轉后的新鏈表頭。更新頭節點
plist->next = p;
將頭節點的next
指向反轉后的新鏈表頭。示例流程
假設鏈表為
頭節點 -> 1 -> 2 -> 3 -> NULL
:
- 初始狀態:
p = 1
,q = 2
,1->next = NULL
。- 第一次循環:
r = 3
,2->next = 1
,p = 2
,q = 3
。- 第二次循環:
r = NULL
,3->next = 2
,p = 3
,q = NULL
。- 結束循環,
頭節點->next = 3
,得到頭節點 -> 3 -> 2 -> 1 -> NULL
。關鍵點
- 三指針協同:
p
、q
、r
分別用于記錄當前節點、下一個節點及臨時保存節點,確保反轉過程中不斷鏈。- 頭節點處理:傳入的
plist
通常為不存儲數據的頭節點,反轉后仍需保持頭節點指向新鏈表頭。- 時間復雜度:O(n),僅需遍歷一次鏈表;空間復雜度O(1),僅使用固定數量的指針。
邊界情況
若鏈表為空或僅有一個節點,斷言會觸發錯誤。實際應用中需根據需求調整斷言條件或添加特殊處理。
?代碼實現:
void Reverse_list2(Node* plist) {assert(plist != NULL);assert(plist->next != NULL);assert(plist->next->next != NULL);Node* p = plist->next;Node* q = p->next;Node* r = NULL;p->next = NULL;while (q != NULL) {r = q->next;q->next = p;p = q;q = r;}plist->next = p;
}
二、如何判斷兩個單鏈表存在交點
思路:兩個鏈表的指針,分別跑到自身的尾節點,判斷是否為同一個尾節點
?代碼實現:
bool Intersect(Node* plist1, Node* plist2) {assert(plist1 != NULL);assert(plist2 != NULL);Node* p = plist1;Node* q = plist2;for (; p->next != NULL; p = p->next);for (; q->next != NULL; q = q->next);return p == q;
}
?三、獲取兩個單鏈表的相交點
思路:
該算法的目標是找到兩個單鏈表的相交節點(如果有)。核心思路是通過對齊兩個鏈表的起始位置,然后同步遍歷,直到找到相同節點。
步驟分解
檢查鏈表是否相交
調用Intersect(plist1, plist2)
函數判斷兩鏈表是否相交。若不相交,直接返回NULL
。計算鏈表長度
通過Get_Length
函數獲取兩個鏈表的長度len1
和len2
,用于后續對齊操作。對齊起始位置
將較長鏈表的指針p
向后移動|len1 - len2|
步,使得剩余未遍歷部分與較短鏈表的長度一致。此時p
和q
(較短鏈表的頭指針)處于同一起跑線。同步遍歷尋找交點
同時移動p
和q
指針,逐個節點比較。當p == q
時,當前節點即為相交節點,返回該節點。關鍵點
- 時間復雜度:O(m+n),其中m和n為兩鏈表長度。需遍歷兩次鏈表(計算長度+同步查找)。
- 空間復雜度:O(1),僅使用常數級額外空間。
- 邊界條件:處理兩鏈表長度差為0時,直接進入同步遍歷階段。
示例說明
假設鏈表1為
A->B->C->D
,鏈表2為E->F->C->D
(相交于節點C):
len1=4
,len2=4
(長度差為0,無需對齊)。- 同步遍歷
p
(鏈表1)和q
(鏈表2),第三次移動時p
和q
均指向C
,返回C
。
代碼實現:
Node* Get_Intersect_Node(Node* plist1, Node* plist2) {bool tag = Intersect(plist1, plist2);if (!tag) return NULL;int len1 = Get_Length(plist1);int len2 = Get_Length(plist2);Node* p = len1 >= len2 ? plist1 : plist2;Node* q = len1 >= len2 ? plist2 : plist1;//此時p指向較長的單鏈表的輔助結點,q指向較長的單鏈表的輔助結點for (int i = 0; i < abs(len1 - len2); i++) {p = p->next;}//pq同步向后走,直到相遇while (p != q) {p = p->next;q = q->next;}return p;
}
?
?