題目描述:
給定兩個單鏈表的頭節點head1和head2,如何判斷兩個鏈表是否相交?相交的話返回true,不想交的話返回false。
給定兩個鏈表的頭結點head1和head2。請返回一個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、有環單鏈表是否相交的判斷方法:先比較兩個鏈表的入環節點是否相等,若想等,則相交,若不想等,則從某個鏈表的入環節點開始循環一周,判斷是否有節點等於另一個鏈表的入環節點,若等於,則相交,否則不相交。
這個有環鏈表的判斷是在得到兩個環的入環節點的基礎上進行的,比較簡單,就不放代碼了。