1.用途
判斷兩個鏈表是否相交有什麼用呢?這是因爲一旦兩個鏈表出現相交的情況,就可能發生這樣的情況,程序釋放了鏈表La的所有節點,這樣就導致了另外一個與之有相交節點的鏈表Lb中的節點也釋放了,而Lb的使用者,可能並不知道事實的真相,這會帶來很大的麻煩。
2.問題
看看兩個鏈表相交到底是怎麼回事吧,有這樣的的幾個事實:
(1)一旦兩個鏈表相交,那麼兩個鏈表中的節點一定有相同地址。
(2)一旦兩個鏈表相交,那麼兩個鏈表從相交節點開始到尾節點一定都是相同的節點。相交指的是結點的地址相同,而不是結點的值相同
給出倆個單向鏈表的頭指針,比如h1,h2,判斷這倆個鏈表是否相交。
爲了簡化問題,我們假設倆個鏈表均不帶環。
問題擴展:
1.如果鏈表可能有環列?
2.如果需要求出倆個鏈表相交的第一個節點列?
參考:http://blog.csdn.net/shiren_bod/article/details/6651703
現在討論一下如何判讀一個鏈表是否有環?
可以這樣做,定義兩個指針,指向頭結點,一個每次移動一個結點,另一個每次移動兩個結點,如果慢的能追上快的(也就是兩個指針重逢),
就說明有環。
舉個例子:就好比跑步,一個跑的快,一個跑的慢,如果跑道是環狀的,跑的快就會把跑的慢的扣圈了
如果跑到無環,那麼就不會扣圈現象產生 。
快的是慢的一倍速度是不會產生跨越的,如果是2倍,3倍就不一定了。 複雜度不會超過2n, 跑的快的會在慢的跑完一圈之前追上的!
可以參考這個博客:http://blog.csdn.net/lock0812/article/details/2644109
下面是July文章上面的解法:
分析:這是來自編程之美上的微軟亞院的一道面試題目。請跟着我的思路步步深入(部分文字引自編程之美):
- 直接循環判斷第一個鏈表的每個節點是否在第二個鏈表中。但,這種方法的時間複雜度爲O(Length(h1) * Length(h2))。顯然,我們得找到一種更爲有效的方法,至少不能是O(N^2)的複雜度。
- 針對第一個鏈表直接構造hash表,然後查詢hash表,判斷第二個鏈表的每個結點是否在hash表出現,如果所有的第二個鏈表的結點都能在hash表中找到,即說明第二個鏈表與第一個鏈表有相同的結點。時間複雜度爲爲線性:O(Length(h1) + Length(h2)),同時爲了存儲第一個鏈表的所有節點,空間複雜度爲O(Length(h1))。是否還有更好的方法呢,既能夠以線性時間複雜度解決問題,又能減少存儲空間?
- 進一步考慮“如果兩個沒有環的鏈表相交於某一節點,那麼在這個節點之後的所有節點都是兩個鏈表共有的”這個特點,我們可以知道,如果它們相交,則最後一個節點一定是共有的。而我們很容易能得到鏈表的最後一個節點,所以這成了我們簡化解法的一個主要突破口。那麼,我們只要判斷倆個鏈表的尾指針是否相等。相等,則鏈表相交;否則,鏈表不相交。
所以,先遍歷第一個鏈表,記住最後一個節點。然後遍歷第二個鏈表,到最後一個節點時和第一個鏈表的最後一個節點做比較,如果相同,則相交,否則,不相交。這樣我們就得到了一個時間複雜度,它爲O((Length(h1) + Length(h2)),而且只用了一個額外的指針來存儲最後一個節點。這個方法時間複雜度爲線性O(N),空間複雜度爲O(1),顯然比解法三更勝一籌。 - 上面的問題都是針對鏈表無環的,那麼如果現在,鏈表是有環的呢?還能找到最後一個結點進行判斷麼?上面的方法還同樣有效麼?顯然,這個問題的本質已經轉化爲判斷鏈表是否有環。那麼,如何來判斷鏈表是否有環呢?
總結:
所以,事實上,這個判斷兩個鏈表是否相交的問題就轉化成了:
1.先判斷帶不帶環
2.如果都不帶環,就判斷尾節點是否相等
3.如果都帶環,判斷一鏈表上倆指針相遇的那個節點,在不在另一條鏈表上。
如果在,則相交,如果不在,則不相交。
算法和July的方法是一樣的,但是實現的方式有點不同而已。
- //判斷鏈表是否有環
- bool IsLoop(node *head, node **firstLoop, node **lastNode)
- {
- node *first = head;
- node *second = head;
- if(NULL == head || NULL == head->next)
- {
- return false;
- }
- do
- {
- first = first->next; //first走一步
- second = second->next->next; //second走兩步
- }while(second && second->next && first != second);
- if(first == second)
- {
- *firstLoop = first; //保存環的首節點
- return true;
- }
- else
- {
- *lastNode = first->next; //保存尾節點
- //printf("%d\n", first->next->data);
- return false;
- }
- return false;
- }
- //判斷兩鏈表是否相交
- //1.如果都不帶環,就判斷尾節點是否相等
- //2.如果都帶環,判斷一鏈表上倆指針相遇的那個節點,在不在另一條鏈表上。
- bool IsCross(node *head1, node *head2)
- {
- node *firstLoop1 = NULL;
- node *firstLoop2 = NULL;
- node *lastNode1 = NULL;
- node *lastNode2 = NULL;
- if(NULL == head1 || NULL == head2)
- {
- printf("Link is Empty!\n");
- exit(1);
- }
- bool isLoop1 = IsLoop(head1, &firstLoop1, &lastNode1);
- bool isLoop2 = IsLoop(head2, &firstLoop2, &lastNode2);
- if(!isLoop1 && !isLoop2) //兩個都無環
- {
- return (lastNode1 == lastNode2);
- }
- else if(isLoop1 != isLoop2) //一個有環,另一個無環
- {
- return false;
- }
- else //兩個都有環,判斷環裏的節點是否能到達另一個鏈表環裏的節點
- {
- node *temp = firstLoop1->next;
- while(temp != firstLoop1)
- {
- if(temp == firstLoop2) //判斷第二個鏈表裏的環節點是否在第一個鏈表中
- return true;
- temp = temp->next;
- }
- }
- return false;
- }
2s = s + nr
s= nr
設整個鏈表長L,入口環與相遇點距離爲x,起點到環入口點的距離爲a。
a + x = nr
a + x = (n – 1)r +r = (n-1)r + L - a
a = (n-1)r + (L – a – x)
{
int isLoop=0;
LinkList *fast,*slow;
fast=list;
slow=list;
while(fast&&fast->next)
{
slow=slow->next;
fast=fast->next->next;//fast每次兩步,slow每次一步
if(fast==slow) //當兩指針相等時,判斷鏈表中有環
{
isLoop=1;
break;
}
}
if(isLoop==1)//鏈表中有環時
{
slow=list;
while(slow!=fast)//一個頭指針,一個相遇點指針,兩個第一次相等時爲環入口點
{
slow=slow->next;
fast=fast->next;
}
return slow;
}
else
{
cout<<"鏈表中沒有環";
return NULL;
}
}
http://blog.163.com/bbluesnow@126/blog/static/27784545201251051156817/