判斷兩個單鏈表是否相交(有環、無環兩種)


題目描述:

  給定兩個單鏈表的頭節點head1和head2,如何判斷兩個鏈表是否相交?相交的話返回true,不想交的話返回false。

  給定兩個鏈表的頭結點head1head2。請返回一個bool值代表它們是否相交。

  鏈表中節點的類型設置如下:

class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}

思路:

  1、首先判斷是否有環,

  • 若兩個鏈表都沒有環,則進行無環單鏈表判斷是否相交,進入2;
  • 若兩個鏈表一個有環一個無環,則直接判斷不相交;
  • 若兩個鏈表都有環,則分別得到每個鏈表的入環節點node1,node2,然后進行有環單鏈表判斷是否相交,進入3;

  判斷是否有環的方法如下:

 1 /**
 2  * 判斷鏈表是否有環
 3  * 判斷方法是設置兩個指針最初均指向頭結點,然后fast每次走2步,slow每次走1步,
 4  * 如果鏈表沒有環,則fast指針肯定先指向表尾的null
 5  * 如果有環,則fast和slow肯定會相遇。然后第一次相遇后我們將fast指針重新指向頭結點,
 6  * 然后fast和slow每次都走一步直到第二次相遇,那么第二次相遇的節點即為入環的節點
 7  * @param head
 8  * @return 若有,則返回入環節點,否則,返回null
 9  */
10 public ListNode judgeRing(ListNode head){        
11     if(head == null){
12         return null ;
13     }
14     ListNode fast = head ;
15     ListNode slow = head ;
16     
17     while(fast != null && fast.next != null){
18         fast = fast.next.next ;
19         slow = slow.next ;
20         if(fast == slow){
21             fast = head ;
22             while(fast != slow){
23                 fast = fast.next ;
24                 slow = slow.next ;
25             }
26             return slow ;
27         }
28     }
29     return null ;
30 }
View Code

  有環時查找入環點的方法的證明過程如下:

  當fast與slow相遇時,slow還沒走完鏈表,而fast已經在環內循環了n圈了,假設slow在相遇前走了s步,則fast走了2s步,設環長為r,有2s=s+nr,即s=nr.

  由上圖可知a+x=s, x+y=r,而我們的目標是找到a的位置。設上圖那個拱起的曲線的長度為y,有a+x=s=nr=(n-1)r+r=(n-1)r+y+x,則a=(n-1)r+y. 這個公式告訴我們,從鏈表頭和相遇點分別設一個指針,每次各走一步,這兩個指針必定相遇,且相遇的第一個點為環入口點

 

  2、無環單鏈表是否相交判斷有多種方法:

    • 方法1:先循環鏈表1,將每個節點的地址進行hash計算存入哈希表,然后計算鏈表2的每個節點的地址的hash值,若與hash表中對應位置有值,則相交,否則不相交。
    • 方法2:見鏈表1與2進行首尾相連,判斷新鏈表是否有環,若沒有,則不相交,若有環,則是相交的。
    • 方法3:先計算兩個鏈表的長度L1、L2,若L1 > L2,則先將鏈表1移動(L1 - L2)個節點,等到鏈表1和鏈表2剩下的長度一樣的時候,一起向后移動,依次判斷當前鏈表的節點是否相等,若相等,則相交,若到隊尾還沒有相等的,則不相交

  方法3的代碼如下:

 1 /**
 2  * 判斷兩個無環鏈表是否相交
 3  * @param head1
 4  * @param head2
 5  * @return
 6  */
 7 public boolean judgeIntersectWithoutRing(ListNode head1,ListNode head2){
 8     int len1 = calLen(head1) ;
 9     int len2 = calLen(head2) ;
10     ListNode intsect = null ;
11     if(len1 > len2){
12         intsect = judge(head1,len1,head2,len2) ;
13     }else{
14         intsect = judge(head2,len2,head1,len1) ;
15     }
16     //判斷相交的節點是否為null,返回結果
17     if(intsect != null){
18         return true ;
19     }else{
20         return false ;
21     }
22 }
23 
24 /**
25  * 計算鏈表的長度並返回
26  * @return
27  */
28 private int calLen(ListNode head){
29     int len = 0;
30     while(head != null){
31         len++ ;
32         head = head.next ;
33     }
34     
35     return len ;
36 }
37 
38 /**
39  * 按長鏈表、短鏈表的順序傳入兩個鏈表,然后進行判斷
40  * 如果相交,則輸出首次相交的節點,否則輸出null
41  * @return
42  */
43 private ListNode judge(ListNode headLong,int lenLong,
44         ListNode headShort,int lenShort){
45     int gap = lenLong - lenShort ;
46     //將較長的鏈表移動到與短鏈表等長的位置
47     while(gap != 0){
48         headLong = headLong.next ;
49     }
50     //開始判斷
51     while(headLong != null && headShort != null){
52         if(headLong == headShort){
53             return headShort ;
54         }else{
55             headLong = headLong.next ;
56             headShort = headShort.next ;
57         }
58     }
59     
60     return null ;
61 }
View Code

   

  3、有環單鏈表是否相交的判斷方法:先比較兩個鏈表的入環節點是否相等,若想等,則相交,若不想等,則從某個鏈表的入環節點開始循環一周,判斷是否有節點等於另一個鏈表的入環節點,若等於,則相交,否則不相交。

  這個有環鏈表的判斷是在得到兩個環的入環節點的基礎上進行的,比較簡單,就不放代碼了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM