slow、fast,初始均指向頭結點。slow 每次移動 1 步,fast 每次移動 2 步。fast 會先到達末尾(NULL),循環結束,返回無環。fast 進入環後,在環內追趕 slow。因相對速度為 1 步/輪,**必定相遇**,返回有環。#include#include #include typedef struct Student { int num; struct Student *pnext; } Stu, *PStu; void link_tail_insert(PStu *phead, PStu *ptail, int val); void link_show(PStu head); void link_judge_loop(PStu head); int main() { PStu phead = NULL, ptail = NULL; int val; while (scanf("%d", &val) != EOF) { link_tail_insert(&phead, &ptail, val); } link_show(phead); // 人為構造環:尾節點指向頭節點(測試用) ptail->pnext = phead; link_judge_loop(phead); return 0; } void link_tail_insert(PStu *phead, PStu *ptail, int val) { PStu pnew = (PStu)malloc(sizeof(Stu)); memset(pnew, 0, sizeof(Stu)); pnew->num = val; if (!*ptail) { *phead = *ptail = pnew; } else { (*ptail)->pnext = pnew; *ptail = pnew; } } void link_show(PStu head) { PStu p = head; if (!head) { puts("empty"); return; } while (p) { printf("%d ", p->num); p = p->pnext; } putchar('\n'); } void link_judge_loop(PStu head) { PStu slow = head, fast = head; while (fast && fast->pnext) { slow = slow->pnext; // 1 步 fast = fast->pnext->pnext; // 2 步 if (slow == fast) { printf("Link has loop\n"); return; } } printf("Link has no loop\n"); }
fast && fast->pnext 確保 fast->pnext->pnext 不解引用空指針。L,快慢指針進環後距離 d,每輪縮短 1,最多 L 輪相遇。O(n),slow 進環前 O(n),進環後 O(L) ≤ O(n)。O(1),僅兩指針。相遇後,將一指針重置為頭結點,兩指針同速(1 步/輪)前進,**再次相遇點即為環入口**。
PStu find_loop_entry(PStu head) {
PStu slow = head, fast = head;
while (fast && fast->pnext) {
slow = slow->pnext;
fast = fast->pnext->pnext;
if (slow == fast) break;
}
if (!fast || !fast->pnext) return NULL; // 無環
slow = head;
while (slow != fast) {
slow = slow->pnext;
fast = fast->pnext;
}
return slow; // 環入口
}
快慢指針法是判斷鏈表環的**標準解法**,無需修改節點、無需額外空間、時間線性。掌握「相遇證明、入口查找、鏈表中點查找」三大快慢指針經典應用,是鏈表題必備基本功。