算法總結之 兩個單鏈表相交的一些列問題
單鏈表,可能有環,也可能無環。給定兩個單鏈表的頭節點head1 和 head2 這兩個鏈表可能交也可能不交。實現函數,如果相交,請返回相交的第一個節點,不交返回null
這道題目需要分析的情況非常
本題拆分長三個子問題,每一個子問題都可以作為一道獨立的算法題。
問題一、 如何判斷有環 有則返回第一個進入環的節點 沒有返回null
問題二、 如何判斷兩個無環鏈表是否相交。相交返回第一個相交節,不相交則返回null
問題三、 如何判斷兩個有環鏈表是否相交。相交返回第一個相交節,不相交則返回null
註意! 如果一個鏈表有環,另一個鏈表無環,他們是不可能相交的。
如果一個鏈表沒有環,遍歷一遍可以遇到鏈表的終點。有環,那麽。。。。永遠在環裏。。。。 如何找到第一個入環節點
思想!!!!! 設置一個慢指針 slow 和一個快指針 fast 開始都是head位置,然後slow 走一步 fast走兩步 遍歷
如果無環,fast一定先遇到終點,一旦fast到達終點,說明鏈表是無環的。
如果有環,fast和slow一定會在環中的某個位置相遇,當fast和slow相遇時候,fast指針重新回到head位置,slow不動。fast從每次移動兩步,改為一步,slow依然一步。繼續遍歷
fast和slow一定會再次相遇。並且在第一個入環的節點處相遇。
瞅瞅 getLoopNode方法
package TT; importTT.Test84.Node; public class Test99 { public Node getLoopNode(Node head){ if(head==null || head.next==null || head.next.next==null){ return null; } Node n1 = head.next; Node n2 = head.next.next; while(n1!=n2){ if(n2.next == null|| n2.next.next==null){ return null; } n2 = n2.next; n1 = n1.next; } n2 = head; while(n1 != n2){ n1=n1.next; n2=n2.next; } return n1; } }
如果解決了問題一,則知道了兩個鏈表有環和無環的情況。
一個有環 一個無環 那麽這兩個鏈表是無論如何也不可能相交的
能相交的情況分兩種: 一種是 都無環 一種是都有環
問題二 判斷兩個無環鏈表相交 交則返回第一個相交節點 不則返回null
如果兩個無環鏈表相交 那麽從相交節點開始一直到兩個鏈表終止的這一段 是連個鏈表共享的
解決:
1 鏈表從1從頭節點開始,走到最後一個節點(不是結束) 統計鏈表1長度len1 同時記錄鏈表1的最後一個節點end1
2 鏈表從2從頭節點開始,走到最後一個節點(不是結束) 統計鏈表2長度len2 同時記錄鏈表1的最後一個節點end2
3 如果 end1 != end2 說明兩個鏈表不交 返回null 如果end1=end2 說明相交
4 如果鏈表1比較長,鏈表1就先走len1-len2步。如果鏈表2比較長,鏈表1就先走len2-len1步。 然後兩個鏈表一起走,第一次走到一起的那個就是所求
實現代碼:
package TT; public class Test { public class Node{ public int value; public Node next; public Node(int data){ this.value=data; } } public Node noLoop(Node head1, Node head2){ if(head1==null || head2 ==null){ return null; } Node cur1 = head1; Node cur2 = head2; int n =0; while(cur1.next != null){ n++; cur1 = cur1.next; } while(cur2.next != null){ n--; cur2=cur2.next; } if(cur1 != cur2){ return null; } cur1 = n>0 ? head1 : head2; cur2 = cur1 == head1 ? head2 : head1; n=Math.abs(n); while(n!=0){ n--; cur1 = cur1.next; } while(cur1 != cur2){ cur1 = cur1.next; cur2 = cur2.next; } return cur1; } }
問題三,如何判斷兩個有環鏈表是否相交,交則返回第一個相交節點,不則返回null
考慮問題三時候,已經得到了兩個鏈表各自的第一個入環節點
拓撲結構的判斷:
讓鏈表1從loop1出發,因為loop1之後的所有節點都在環上,所以將來一定能回到loop1 如果回到loop1之前沒有遇到loop2 不想交(左圖)
如果遇到了loop2 說明相交(右圖) 此時返回loop1 跟 loop2 都可以
代碼:
package TT; public class Test100 { public class Node{ public int value; public Node next; public Node(int data){ this.value=data; } } public Node bothLoop(Node head1 ,Node loop1, Node head2, Node loop2){ Node cur1 = null; Node cur2 = null; if(loop1 == loop2){ cur1 = head1; cur2 = head2; int n=0; while(cur1 != loop1){ n++; cur1 = cur1.next; } while(cur2 !=loop2){ n--; cur2=cur2.next; } cur1 = n>0 ? head1 : head2; cur2 = cur1==head1? head2 : head1; while(n!=0){ n--; cur1=cur1.next; } while(cur1 != cur2){ cur1 =cur1.next; cur2 =cur2.next; } return cur1; }else{ cur1 = loop1.next; //進環測試 while(cur1 != loop1){ if(cur1==loop2){ return loop1; } cur1=cur1.next; } return null; } } }
全部代碼:
package TT; public class Test101 { public class Node{ public int value; public Node nextNode; public Node(int data){ this.value=data; } } public Node getIntersectNode(Node head1, Node head2){ if(head1 == null || head2==null){ return null; } Node loop1= getLoopNode(head1); //獲得環形節點 Node loop2 =getLoopNode(head2); if(loop1==null && loop2==null){ return noLoop(head1, head2); } if(loop1 !=null && loop2 != null){ return bothLoop(head1, loop1, head1 loop2); } return null; } }
算法總結之 兩個單鏈表相交的一些列問題