題幹
Given a linked list, return the node where the cycle begins. If there is no cycle, returnnull.
Follow up:
Can you solve it without using extra space?
鏈表是否有環?如果有找到環的入口結點。
是否可以不開闢額外空間。
數據結構
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
解題思路
1.有額外空間的話是可以用vector等數據結構,遍歷結點,遍歷結點的時候存儲訪問過的結點,用O(1)的時間可以檢索是否已經存儲過相同結點。如果有相同結點,便得到了入口結點;如果沒找到,便添加當前結點到vector中。
2.沒有空間開銷的方法分幾步:
a.判斷鏈表是否有環:用兩個指針,一慢p1一快p2,遍歷鏈表,如果兩個指針相遇,則說明有環。
b.得到環中結點的數量:a步當中相遇的快慢指針位置,一個指針固定p2,另一個p1以步長爲1繼續遍歷,並計數,再次相遇得到環中結點的數量cyc_num。
c.得到環入口結點:置p1,p2兩指針爲頭結點head,先讓p2結點向後走cyc_num步,然後p1,p2一起以步長爲1遍歷鏈表。相遇得到的結點即爲入口結點。
參考代碼
方法一:
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if (head==NULL) return NULL;
ListNode *cur=head;
vector<ListNode *>data;//建立vector以存儲經過的結點
while(cur->next!=NULL){
vector<ListNode *>::iterator s=find(data.begin(),data.end(),cur);//O(1)時間查找,是否存儲過當前結點
if (s!=data.end())//存儲過當前結點
return cur;
data.push_back(cur);//添加結點到vector中
cur=cur->next;
}
return NULL;
}
};
方法二:
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if (head==NULL) return NULL;
ListNode *p1=head,*p2=head;
int cyc_num=1;
//第一步:判斷是否存在環
while(p2->next!=NULL&&p2->next->next!=NULL){
p1=p1->next;//步長爲1
p2=p2->next->next;//步長爲2
if (p1==p2){//如果存在環
//第二步:確定環中結點數量
while(p1->next!=p2){
p1=p1->next;
cyc_num++;
}
//第三步:找到入口結點
p1=head;p2=head;
for(int i=0;i<cyc_num;i++)
p2=p2->next;
while(p1!=p2){
p1=p1->next;
p2=p2->next;
}
return p1;
}
}
return NULL;
}
};
方法討論
方法1代碼簡單但需要額外開銷。
方法2代碼相對複雜但是沒有額外的空間開銷。
綜上兩個方法應用場景不同,所以各有用處。