判斷兩個連結串列是否相交
阿新 • • 發佈:2022-04-10
題目描述:
判斷兩個連結串列(可能有環可能無環)之間是否有相交(此處相交指連結串列經過了同一個地址的節點(與節點的值無關)) 。如果相交,則返回相交的第一個節點。否則,返回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; 8boolean 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 }