1. 程式人生 > 其它 >判斷兩個單鏈表是否相交(有環、無環兩種)

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

題目描述:

  給定兩個單鏈表的頭節點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 }

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

  當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 }

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

  這個有環連結串列的判斷是在得到兩個環的入環節點的基礎上進行的,比較簡單,就不放程式碼了。