判斷兩個鏈表是否相交

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文章上面的解法:

分析:這是來自編程之美上的微軟亞院的一道面試題目。請跟着我的思路步步深入(部分文字引自編程之美):

  1. 直接循環判斷第一個鏈表的每個節點是否在第二個鏈表中。但,這種方法的時間複雜度爲O(Length(h1) * Length(h2))。顯然,我們得找到一種更爲有效的方法,至少不能是O(N^2)的複雜度。
  2. 針對第一個鏈表直接構造hash表,然後查詢hash表,判斷第二個鏈表的每個結點是否在hash表出現,如果所有的第二個鏈表的結點都能在hash表中找到,即說明第二個鏈表與第一個鏈表有相同的結點。時間複雜度爲爲線性:O(Length(h1) + Length(h2)),同時爲了存儲第一個鏈表的所有節點,空間複雜度爲O(Length(h1))。是否還有更好的方法呢,既能夠以線性時間複雜度解決問題,又能減少存儲空間?
  3. 進一步考慮“如果兩個沒有環的鏈表相交於某一節點,那麼在這個節點之後的所有節點都是兩個鏈表共有的”這個特點,我們可以知道,如果它們相交,則最後一個節點一定是共有的。而我們很容易能得到鏈表的最後一個節點,所以這成了我們簡化解法的一個主要突破口。那麼,我們只要判斷倆個鏈表的尾指針是否相等。相等,則鏈表相交;否則,鏈表不相交。
    所以,先遍歷第一個鏈表,記住最後一個節點。然後遍歷第二個鏈表,到最後一個節點時和第一個鏈表的最後一個節點做比較,如果相同,則相交,否則,不相交。這樣我們就得到了一個時間複雜度,它爲O((Length(h1) + Length(h2)),而且只用了一個額外的指針來存儲最後一個節點。這個方法時間複雜度爲線性O(N),空間複雜度爲O(1),顯然比解法三更勝一籌。
  4. 上面的問題都是針對鏈表無環的,那麼如果現在,鏈表是有環的呢?還能找到最後一個結點進行判斷麼?上面的方法還同樣有效麼?顯然,這個問題的本質已經轉化爲判斷鏈表是否有環。那麼,如何來判斷鏈表是否有環呢?

總結:
所以,事實上,這個判斷兩個鏈表是否相交的問題就轉化成了:
1.先判斷帶不帶環
2.如果都不帶環,就判斷尾節點是否相等
3.如果都帶環,判斷一鏈表上倆指針相遇的那個節點,在不在另一條鏈表上。
如果在,則相交,如果不在,則不相交。

下面先給出是否有環的算法,就使用前面我的文章中的方法,但是題目的需要,增加了保存了尾節點。

算法和July的方法是一樣的,但是實現的方式有點不同而已。

  1. //判斷鏈表是否有環  
  2. bool IsLoop(node *head, node **firstLoop, node **lastNode)  
  3. {  
  4.     node *first = head;  
  5.     node *second = head;  
  6.   
  7.     if(NULL == head || NULL == head->next)  
  8.     {  
  9.         return false;  
  10.     }  
  11.   
  12.     do  
  13.     {  
  14.         first = first->next;         //first走一步  
  15.         second = second->next->next;  //second走兩步  
  16.     }while(second && second->next && first != second);  
  17.   
  18.     if(first == second)  
  19.     {  
  20.         *firstLoop = first;     //保存環的首節點  
  21.         return true;  
  22.     }  
  23.     else  
  24.     {  
  25.         *lastNode = first->next; //保存尾節點  
  26.         //printf("%d\n", first->next->data);  
  27.         return false;  
  28.     }  
  29.     return false;  
  30. }  
接下來是判斷是否相交的算法,和July的代碼是一樣的,只是自己寫了一遍。學習了.....

  1. //判斷兩鏈表是否相交  
  2. //1.如果都不帶環,就判斷尾節點是否相等    
  3. //2.如果都帶環,判斷一鏈表上倆指針相遇的那個節點,在不在另一條鏈表上。    
  4. bool IsCross(node *head1, node *head2)  
  5. {  
  6.     node *firstLoop1 = NULL;  
  7.     node *firstLoop2 = NULL;  
  8.     node *lastNode1 = NULL;  
  9.     node *lastNode2 = NULL;  
  10.   
  11.     if(NULL == head1 || NULL == head2)  
  12.     {  
  13.         printf("Link is Empty!\n");  
  14.         exit(1);  
  15.     }  
  16.     bool isLoop1 = IsLoop(head1, &firstLoop1, &lastNode1);  
  17.     bool isLoop2 = IsLoop(head2, &firstLoop2, &lastNode2);  
  18.   
  19.     if(!isLoop1 && !isLoop2)    //兩個都無環  
  20.     {  
  21.         return (lastNode1 == lastNode2);  
  22.     }  
  23.   
  24.     else if(isLoop1 != isLoop2) //一個有環,另一個無環  
  25.     {     
  26.         return false;  
  27.     }  
  28.     else    //兩個都有環,判斷環裏的節點是否能到達另一個鏈表環裏的節點  
  29.     {  
  30.         node *temp = firstLoop1->next;  
  31.         while(temp != firstLoop1)  
  32.         {  
  33.             if(temp == firstLoop2)  //判斷第二個鏈表裏的環節點是否在第一個鏈表中  
  34.                 return true;  
  35.             temp = temp->next;  
  36.         }  
  37.     }  
  38.     return false;  
  39. }  
二、鏈表中含有環    http://www.cnblogs.com/zhyg6516/archive/2011/03/29/1998831.html爲什麼會相遇
鏈表中有環如下圖:
判斷兩個鏈表是否相交 - 楓葉 - 楓葉
1.鏈表中是否有環的判斷
可以設置兩個指針(fast,slow),初始值均指向頭,slow每次向前一步,fast每次向前兩步;
如果鏈表中有環,則fast先進入環中,而slow後進入環中,兩個指針在環中必定相遇;
如果fast遍歷到尾部爲NULL,則無環
2.鏈表有環,判斷環的入口點。參考: http://www.cnblogs.com/BeyondAnyTime/archive/2012/07/06/2580026.html
  當fast若與slow相遇時,slow肯定沒有走遍歷完鏈表,而fast已經在環內循環了n圈(1<=n)。假設slow走了s步,則fast走了2s步(fast步數還等於s 加上在環上多轉的n圈),設環長爲r,則:

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)

(L – a – x)爲相遇點到環入口點的距離,由此可知,從鏈表頭到環入口點等於(n-1)循環內環+相遇點到環入口點

因而,可以在鏈表頭,相遇點分別設定一個指針,每次各走一步,兩個指針必定相遇,則相遇第一點爲環入口點
LinkList *  ListLoop(LinkList *list) //判斷一個鏈表中是否有環
{
 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;
  }
當兩個鏈表中有環時,相交的判斷:
(1)首先分別找出兩個鏈表入環的第一個結點記爲p1,p2
 (2)如果p1==p2,說明兩個鏈表在入環之前或入環的第一個結點相交;則此時可以轉爲兩個鏈表均不帶環相交的判斷,把p1,p2當作最後的末尾結點
(3)如果p1!=p2,此時兩個鏈表可能完全不相交;也可能兩個鏈表完全共有同一個環。
此時,判斷p1->next與p2->next是否相等,如果相等則兩鏈表完全共環;如果不相等,則完全不相交
 
當一個鏈表中有環,一個鏈表中沒有環時,兩個鏈表必不相交。

http://blog.163.com/bbluesnow@126/blog/static/27784545201251051156817/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章