1. 程式人生 > 其它 >判斷兩個連結串列是否相交

判斷兩個連結串列是否相交

題目描述:

判斷兩個連結串列(可能有環可能無環)之間是否有相交(此處相交指連結串列經過了同一個地址的節點(與節點的值無關)) 。如果相交,則返回相交的第一個節點。否則,返回null

前置知識點:判斷連結串列是否有環,並找出第一個成環節點:

法1:使用HashSet的方法來判斷是否經過了重複節點

程式碼及解析:

 1 public static Node getLoopList1(MyLink list) {
 2         Node p = list.head;
 3         HashSet<Node> set = new HashSet<>();//利用HashSet中元素不可重複的特性
4 while (p != null) { 5 if (set.contains(p) == false) { 6 set.add(p); 7 } 8 else {//出現重複節點,說明有環結構 9 return p; 10 } 11 p = p.next; 12 } 13 return null; 14 }

 

法2:使用快慢指標的方法來判斷

程式碼及解析:

 1 public static Node getLoopList2(MyLink list) {
 2         //因為快指標第一次要走兩步,為了避免越界,先討論掉這些情況
 3         if (list.head == null || list.head.next == null || list.head.next.next == null) {
 4             return null;
 5         }
 6         //建立一個快慢指標
 7         Node fast = list.head, slow = list.head;
 8
boolean isChainList = false; 9 while (fast.next != null && fast != null) {//如果有環,快慢指標一定會相遇 10 fast = fast.next.next; 11 slow = slow.next; 12 if (slow == fast) { 13 isChainList = true; 14 break; 15 } 16 } 17 if (isChainList == false) { 18 return null; 19 } 20 //將快指標移回起點,慢指標原地不動,兩者一次移動一格,則相遇的點為環的起點(證明過程複雜) 21 fast = list.head; 22 while (fast != slow) { 23 fast = fast.next; 24 slow = slow.next; 25 } 26 return fast; 27 }

 

在前置知識點的基礎上,接下來來解決這個問題

分三類情況討論:

1.兩個連結串列都是無環的:

思路:分別得到兩個連結串列的長度差值,然後讓長的那條連結串列指標走差值步後,讓兩個連結串列的指標同時向右走,先判斷尾節點是否相同,如果相同則有相交節點,防止則無相交節點
      當兩個節點指向同一個節點時,則返回這個所要的節點

程式碼及解析:

 1 public static Node noLoop(MyLink list1,MyLink list2) {
 2         int n = 0;//獲得兩連結串列長度的差值 
 3         Node cur1 = list1.head, cur2 = list2.head;
 4         while (cur1.next != null) {
 5             n++;
 6             cur1 = cur1.next;
 7         }
 8         while (cur2.next != null) {
 9             n--;
10             cur2 = cur2.next;
11         }
12         if (cur1 != cur2) {//兩連結串列尾節點不同,兩者一定沒有相交節點
13             return null;
14         }
15         //已經確定一定有相交節點,現在找出第一個相交節點
16         cur1 = n > 0 ? list1.head : list2.head;//cur1為長連結串列的指標
17         cur2 = n > 0 ? list2.head : list1.head;//cur2為短連結串列的指標
18         //讓長連結串列先走n步
19         n = Math.abs(n);
20         for (int i = 0; i < n; i++) {
21             cur1 = cur1.next;
22         }
23         //之後讓兩連結串列指標同時右走,直到指向相同節點為止
24         while (cur1 != cur2) {//
25             cur1 = cur1.next;
26             cur2 = cur2.next;
27         }
28         return cur1;
29     }

 

2.一個連結串列有環一個連結串列無環:一定沒有相交的節點(可通過畫圖分析證明)

 

3.兩個連結串列都有環:

思路:在有相交情況下,要分兩種情況考慮:
(1).在成環之前就已經相交:則將其轉為無環的情況,把第一個成環節點作為尾節點即可
(2).在環中有相交:則遍歷其中一個連結串列(直到轉了一圈為止),如果經過了另一個連結串列的環起始點則有相交

程式碼及解析:

 1 public static Node bothLoop(MyLink list1,Node loop1,MyLink list2,Node loop2) {
 2         Node cur1 = list1.head;
 3         Node cur2 = list2.head;
 4         if (loop1 == loop2) {//在成環前就相交的情況下,尋找相交的第一個節點(轉成無環模型)
 5             int n = 0;
 6             while (cur1.next != loop1) {
 7                 n++;
 8                 cur1 = cur1.next;
 9             }
10             while (cur2.next != loop1) {
11                 n--;
12                 cur2 = cur2.next;
13             }
14             cur1 = n > 0 ? list1.head : list2.head;
15             cur2 = n > 0 ? list2.head : list1.head;
16             n = Math.abs(n);
17             for (int i = 0; i < n; i++) {
18                 cur1 = cur1.next;
19             }
20             while (cur1 != cur2) {
21                 cur1 = cur1.next;
22                 cur2 = cur2.next;
23             }
24             return cur1;
25         }
26         else {//說明就算有相交節點也在環後  或  根本沒有相交節點
27             cur1 = loop1.next;//直接從環中去尋找有沒有相交節點
28             while (cur1 != loop1) {
29                 if (cur1 == loop2) {
30                     return loop2;//也可以return loop1,兩者都可認為是相交的第一個節點
31                 }
32                 cur1 = cur1.next;
33             }
34             return null;//否則就沒有公共節點
35         }
36     }

 

最後對這三種情況進行總結,得到一個總結性函式:

 1 //最後對三種情況進行總結得到一個函式:
 2     public static Node getIntersectNode(MyLink list1,MyLink list2) {
 3         Node head1 = list1.head;
 4         Node head2 = list2.head;
 5         if (head1 == null || head2 == null) {
 6             return null;
 7         }
 8         Node loop1 = ChainList.getLoopList2(list1);
 9         Node loop2 = ChainList.getLoopList2(list2);
10         if (loop1 == null && loop2 == null) {
11             return noLoop(list1,list2);
12         }
13         else if (loop1 != null && loop2 != null) {
14             return bothLoop(list1,loop1,list2,loop2);
15         }
16         else {
17             return null;
18         }
19     }