標籤:style blog http color io strong for div 代碼
LeetCode: Linked List Cycle
Given a linked list, determine if it has a cycle in it.
Follow up:
Can you solve it without using extra space?
地址:https://oj.leetcode.com/problems/linked-list-cycle/
演算法:將鏈表的指標倒過來,如果最後迴圈終點是原來鏈表的前端節點,說明鏈表有環,如果迴圈的終點不是鏈表的前端節點,那麼說明鏈表沒有環(只有一個節點的鏈表要單獨判斷)。例如,有如下鏈表:1->2->3->4->5->6->4(有環的鏈表,從最後一個節點6又指回第4個節點),將鏈表的指標倒過來後變成:1->2->3->4<-5<-6<-4(表示第4個節點指像第6個節點),注意前面四個節點的順序被倒置了兩次,因為如果有環的話迴圈會回到第4個節點,然後從第4個節點又迴圈到前端節點。這裡不足的地方是改變了原來的鏈表,但如果你再次從前端節點開始將鏈表指標在倒置一次則又會回到原來的鏈表。代碼:
1 /** 2 * Definition for singly-linked list. 3 * struct ListNode { 4 * int val; 5 * ListNode *next; 6 * ListNode(int x) : val(x), next(NULL) {} 7 * }; 8 */ 9 class Solution {10 public:11 bool hasCycle(ListNode *head) {12 if (!head)13 return false;14 if(!head->next)15 return false;16 if(head->next == head)17 return true;18 ListNode *p = head;19 ListNode *pre = NULL;20 ListNode *old_head = head;21 head = NULL;22 while(p){23 pre = p;24 p = p->next;25 pre->next = head;26 head = pre;27 }28 if(pre == old_head)29 return true;30 return false;31 }32 };
上述的方法可以利用常數空間來判斷一個鏈表是否存在環,但如果題目要求找到這個環的起點,那麼上述方法應該無法利用常數空間來解決了。以下的方法是參考網上的答案,這種方法不僅可以判斷一個鏈表是否存在環,而且可以找到該環的起點節點。具體的思想:初始化兩個指標,一個為slow,一個為fast,二者的初始值都為表前端節點,然後每次讓slow往後走一個節點,fast往後走兩個節點。若沒有環,則二者肯定不會相遇;若存在環,則二者肯定會在環中某個節點相遇。證明如下:
假設從表前端節點到環開始的節點長度為a,環的總長度為k,設slow在所在節點距離環開始處的距離為b,由於slow每次只走一步,則b的取值範圍為[0,k-1],
另外設c=k-b。舉個例子,假如鏈表為1->2->3->4->5->6->4,則a=3,k=4,若slow位於第4個節點,則b=0,c=4;若slow位於第6個節點,則b=3,c=1。現在假設slow走的總步數為a+b+tk,其中t表示slow在環中迴圈了t次,那麼由於fast走的步數是slow的兩倍,故fast走的總步數為2(a+b+tk),所以fast將停留的節點距離環開始的節點距離為a+2b,現在假如我們能夠找到一個自然數n,使得a+b=nk,那麼我們就可以保證fast停留的位置與slow的位置一樣。很顯然,由於b可以取0到k-1中的任意值,故無論a為何值,我們總能找到一個滿足a+b=nk的b值,即slow指標和fast指標最終必定會相遇。具體代碼如下:
1 /** 2 * Definition for singly-linked list. 3 * struct ListNode { 4 * int val; 5 * ListNode *next; 6 * ListNode(int x) : val(x), next(NULL) {} 7 * }; 8 **/ 9 class Solution {10 public:11 bool hasCycle(ListNode *head) {12 if (!head || !head->next){13 return false;14 }15 if (head->next == head){16 return true;17 }18 ListNode *fast = head;19 ListNode *slow = head;20 while(fast && slow){21 slow = slow->next;22 fast = fast->next;23 if(fast) fast = fast->next;24 if(fast == slow){25 break;26 }27 }28 if(fast)29 return true;30 else31 return false;32 }33 };
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?
地址:https://oj.leetcode.com/problems/linked-list-cycle-ii/
演算法:第二題與第一題不同的是,不僅要判斷鏈表是否存在環,而且如果存在環的話還要找到環開始的起點。這裡所用的方法也是接上面的方法,假如存在環的話,那麼利用上面的方法,slow跟fast能夠在環當中的某個節點相遇,並且跳出迴圈。此時,固定住fast不變,將slow設定為表前端節點,然後讓二者同步的往後走,則在經過a步後二者必定相遇,且相遇的節點必為環的開始節點。證明如下:
假設經過第一題的方法後,slow和fast在距離環開始節點為b時相遇,另在假設fast指標經過a步後繞環走了t圈,並且最終停留在距離環開始節點為d時停下來,現在我們要證明的是d=0。根據題意可知
d=a-tk-c=a-tk-(k-b)=a+b-(t+1)k,根據上面一題的證明可知a+b能夠被k整除,故d一定等於零,也就是說fast在經過a步後一定停留在環開始的節點。第二題代碼:
1 /** 2 * Definition for singly-linked list. 3 * struct ListNode { 4 * int val; 5 * ListNode *next; 6 * ListNode(int x) : val(x), next(NULL) {} 7 * }; 8 */ 9 class Solution {10 public:11 ListNode *detectCycle(ListNode *head) {12 if (!head || !head->next){13 return NULL;14 }15 if (head->next == head){16 return head;17 }18 ListNode *fast = head;19 ListNode *slow = head;20 while(fast && slow){21 slow = slow->next;22 fast = fast->next;23 if(fast) fast = fast->next;24 if(fast == slow){25 break;26 }27 }28 if(fast != slow){29 return NULL;30 }31 slow = head;32 while(fast != slow){33 fast = fast->next;34 slow = slow->next;35 }36 return fast;37 }38 };
LeetCode: Linked List Cycle