題目:
Given a linked list, return the node where the cycle begins. If there is no cycle, return null
.
Follow up:
Can you solve it without using extra space?
解題思路:
判斷連結串列有無環,可用快慢指標進行,快指標每次走兩步,慢指標每次走一步,如果快指標追上了慢指標,則存在環,否則,快指標走到連結串列末尾即為NULL是也沒追上,則無環。
為什麼快慢指標可以判斷有無環?
因為快指標先進入環,在慢指標進入之後,如果把慢指標看作在前面,快指標在後面每次迴圈都向慢指標靠近1,所以一定會相遇,而不會出現快指標直接跳過慢指標的情況。
如何找到環的入口點呢?
我們先看圖再說話:
從圖各段我們分析,因為quick指標每次走兩步二slow指標每次走一步,所以當兩指標相遇時,quick走了兩倍的slow指標所走長度即:假設相遇點為z點
a + b + n * ( b + c ) = 2 * (a + b) 公式1
整理得:
a = n * (b + c) – b 公式2
根據公式2可知,要找到環入口點,可使用兩個指標,p1和p2,p1從連結串列頭開始走,p2從z點即快慢指標相遇點開始走,當p1指標走到Y(環入口點)時即長度為a時,p1走了n * (b + c) – b,可知p1也正好在Y點,所以利用p1和p2兩指標,當它們相遇時,相遇點即為環入口點。
實現程式碼:
#include <iostream> using namespace std; /** Linked List Cycle II */ struct ListNode { int val; ListNode *next; ListNode(int x) : val(x), next(NULL) {} }; void addNode(ListNode* &head, int val) { ListNode *node = new ListNode(val); if(head == NULL) { head = node; } else { node->next = head; head = node; } } void printList(ListNode *head) { while(head) { cout<<head->val<<" "; head = head->next; } } class Solution { public: ListNode *detectCycle(ListNode *head) { if(head == NULL || head->next == NULL) return NULL; ListNode *quick = head; ListNode *slow = head; while(quick && quick->next)//利用快慢指標判斷有無環 { quick = quick->next->next; slow = slow->next; if(quick == slow) break; } if(quick != slow) return NULL; //slow指標從頭開始走,quick指標從相遇點開始走,根據公式可知,相遇點即為環入口點 slow = head; while(slow != quick) { slow = slow->next; quick = quick->next; } return slow; } }; int main(void) { ListNode *head = new ListNode(1); ListNode *node1 = new ListNode(2); ListNode *node2 = new ListNode(3); ListNode *node3 = new ListNode(4); head->next = node1; node1->next = node2; node2->next = node3; node3->next = node1; Solution solution; ListNode *rNode = solution.detectCycle(head); if(rNode) cout<<rNode->val<<endl; return 0; }