題目描述:給一個(gè)鏈表,判斷其中環(huán)的起始結(jié)點(diǎn),若沒(méi)有環(huán)則返回null。要求不改變鏈表,空間復(fù)雜度O(1)。
分析:這題與141題是同一個(gè)算法引出的一系列問(wèn)題,即Floyd判圈算法,又稱龜兔賽跑算法,可以在有限狀態(tài)機(jī)、迭代函數(shù)或者鏈表上判斷是否存在環(huán),并求出該環(huán)的起點(diǎn)與長(zhǎng)度的算法。
算法的核心思想是設(shè)置快慢指針,例如設(shè)fast指針的速度是slow指針的2倍,若兩指針能相遇則說(shuō)明有環(huán),否則無(wú)環(huán)。
例如有環(huán)鏈表如下圖:
算法可以解決的問(wèn)題有:
- 判斷鏈表中是否有環(huán),即141題。
若就是一個(gè)循環(huán)鏈表,即Y點(diǎn)與X點(diǎn)是同一點(diǎn),鏈表的尾結(jié)點(diǎn)鏈到頭結(jié)點(diǎn),此時(shí)由于fast指針?biāo)俣仁莝low指針的2倍,故起點(diǎn)就是兩者相遇點(diǎn),每當(dāng)slow指針回到起點(diǎn)則相遇,因?yàn)閟low指針每走一圈即fast指針正好走兩圈。
若是像上圖的一般情況,則當(dāng)slow指針到達(dá)Y處時(shí)fast指針已經(jīng)繞圈走了一段距離,故不一定會(huì)在Y處相遇。列方程可得這是一個(gè)只與時(shí)間有關(guān)的方程,即過(guò)一段時(shí)間一定可以相遇。
若兩指針在fast走到末尾還未相遇,則說(shuō)明無(wú)環(huán)。
- 計(jì)算環(huán)的長(zhǎng)度。
第一種方法:第一次相遇后,讓slow繼續(xù)走,記錄到下次遇到fast時(shí),即到達(dá)Z點(diǎn)時(shí),slow走過(guò)的結(jié)點(diǎn)數(shù)。此時(shí)slow走了一圈,正好還在Z點(diǎn)相遇。
第二種方法:設(shè)環(huán)的長(zhǎng)度為L(zhǎng),第一次相遇時(shí)slow走過(guò)的距離:a+b,fast走過(guò)的距離:a+b+c+b。因?yàn)閒ast的速度是slow的兩倍,所以fast走的距離是slow的兩倍,有 2(a+b) = a+b+c+b,得到a=c。發(fā)現(xiàn)L=b+c=a+b,也就是說(shuō),從一開(kāi)始到二者第一次相遇,走過(guò)的結(jié)點(diǎn)數(shù)就等于環(huán)的長(zhǎng)度。
找到環(huán)中第一個(gè)節(jié)點(diǎn),即Y點(diǎn)。
根據(jù)結(jié)論a=c,讓兩個(gè)指針?lè)謩e從X和Z開(kāi)始走,每次走一步,則正好會(huì)在Y相遇,也就是環(huán)的第一個(gè)節(jié)點(diǎn)。將有環(huán)的鏈表變成單鏈表(解除環(huán))
在上一個(gè)問(wèn)題的最后,將c段中Y點(diǎn)之前的結(jié)點(diǎn)與Y的鏈接切斷即可。判斷兩個(gè)單鏈表是否有交點(diǎn),并找到第一個(gè)相交的結(jié)點(diǎn)。
先判斷兩個(gè)鏈表是否有環(huán),如果一個(gè)有環(huán)一個(gè)沒(méi)環(huán),肯定不相交;如果兩個(gè)都沒(méi)有環(huán),則判斷兩個(gè)列表的尾部是否相等;如果兩個(gè)都有環(huán),判斷一個(gè)鏈表上的Z點(diǎn)是否在另一個(gè)鏈表上。
分別計(jì)算兩個(gè)鏈表的長(zhǎng)度len1,len2,假設(shè)鏈表1較長(zhǎng),首先對(duì)較長(zhǎng)的鏈表遍歷len1 - len2個(gè)結(jié)點(diǎn)到結(jié)點(diǎn)p,此時(shí)結(jié)點(diǎn)p與鏈表2頭結(jié)點(diǎn)到兩者相交的結(jié)點(diǎn)的距離相同,在此時(shí)同時(shí)遍歷兩個(gè)鏈表,直到相遇的結(jié)點(diǎn)為止,這個(gè)結(jié)點(diǎn)就是相交的結(jié)點(diǎn)。
在slow指針入環(huán)后,在最多走一圈的時(shí)間內(nèi)必將遇到fast,時(shí)間復(fù)雜度O(n),空間O(1)。
代碼:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode *slow = head, *fast = head;
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
if ( slow == fast) //找到相遇點(diǎn)
{
ListNode *p = head;
while( p != slow) //slow從Z點(diǎn)出發(fā),新指針P從起點(diǎn)X出發(fā)同時(shí)走,直到相遇
{
p = p->next;
slow = slow->next;
}
return p;
}
}
return nullptr;
}
};