Linked List Cycle II的總結
關鍵知識
鏈表的處理
解題思路
一種簡單的做法就是順序遍歷每個結點,然後每次都判斷該點是否在前面遍歷過的結點中,可以採用兩層遍歷的做法或者是採用set來存儲前面的結點,然後每次在set中find這個結點。但是set會將空間複雜度大大增加。
另一種做法就是採用快慢兩個指針在鏈表中遍歷,如果快慢指針會相遇,則說明鏈表中存在環,否則不存在。存在環後,再通過一定的數學方法找到起始結點。該做法的時間複雜度爲O(n),空間複雜度爲O(1),爲最佳做法。
簡單做法
首先初始化一個set,然後遍歷鏈表,不斷地比較該節點是否出現過,出現過則說明存在環,沒出現過就將此節點加進set,直到遍歷完整個鏈表。
時間複雜度: 平均爲O(n),最差爲O(n^2)
空間複雜度: O(n)
最佳解法
採用兩個指針分別爲fast和slow,fast指針遍歷的速度爲slow的兩倍,如果在fast到達鏈表結尾時兩個指針不相遇,則說明鏈表不存在環,如果存在環,由於兩者速度不一樣,必定相遇,且相遇也說明了鏈表存在環。相遇之後,用meet指針記錄相遇結點,然後用p指針指向起始結點head,然後兩個指針同步遍歷,直到兩者相遇的結點即爲環的起始結點。證明過程:
時間複雜度: O(n)
空間複雜度: O(1)
題目
簡答做法
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
set<ListNode*> sl;
ListNode* p = head;
while (p) {
if (sl.find(p) == sl.end()) {
sl.insert(p);
p = p->next;
} else {
return p;
}
}
return nullptr;
}
};
最優解法
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode *fast = head, *slow = head, *meet = nullptr;
if (!head) return nullptr;
fast = head->next ? head->next->next : head->next;
slow = head->next;
while (fast) {
if (fast == slow) {
meet = slow;
break;
}
fast = fast->next ? fast->next->next : fast->next;
slow = slow->next;
}
if (!meet) return nullptr;
ListNode* p = head;
while (p != meet) {
meet = meet->next;
p = p->next;
}
return meet;
}
};