兩個單鏈表相交的一系列問題
【 題目】 給定兩個可能有環也可能無環的單鏈表, 頭節點head1和head2。 請實現一個函數, 如果兩個鏈表相交, 請返回相交的 第一個節點。 如果不相交, 返回null
【 要求】 如果兩個鏈表長度之和爲N, 時間複雜度請達到O(N), 額外空間複雜度請達到O(1)。
前言:
肯定不可能一個有環,一個無環,然後相交了。
首先,我們判斷是有環還是無環,如果有環,返回入環的第一個節點。無環null。
簡單方法:採用hashset,不斷next往裏面賦值,如果hashset裏有重複的node出現,則這個節點就是,如果next出現了null就無環。
省空間的做法:快慢指針。當快指針與慢指針相遇,則有環,此時將快指針=head,然後快指針和慢指針都變成1步長,再相遇的地方就是入環節點。中途兩個指針如果遇到null就是無環的結構。
public static Node getLoopNode(Node head) {
if (head == null || head.next == null || head.next.next == null) {
return null;
}
Node n1 = head.next; // n1 -> slow
Node n2 = head.next.next; // n2 -> fast
while (n1 != n2) {
if (n2.next == null || n2.next.next == null) {
return null;//無環
}
n2 = n2.next.next;
n1 = n1.next;
}
n2 = head; // n2 -> walk again from head
while (n1 != n2) {
n1 = n1.next;
n2 = n2.next;
}
return n1;
}
第二點學習:如果兩個無環,找相交
先判斷是否相交,兩個都遍歷到最後一個非null的node,如果相等則相交,如果不等肯定不相交。
相交之後,把長的那一個,先走完(長鏈表-短鏈表)插值的長度,然後和慢的一起走,當第一次相遇,返回。
public static 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) {//當cur1變成最後一個就出來了
n++;//cur的總數
cur1 = cur1.next;
}
while (cur2.next != null) {
n--;//得到差值
cur2 = cur2.next;
}
if (cur1 != cur2) {//如果兩個無環鏈表的最後一個節點不相等,那麼不相交
return null;
}
cur1 = n > 0 ? head1 : head2;//誰長誰的head變成cur1
cur2 = cur1 == head1 ? head2 : head1;//誰短誰的head變成cur2
n = Math.abs(n);
while (n != 0) {//先走兩個鏈表的差值
n--;
cur1 = cur1.next;
}
while (cur1 != cur2) {//然後一起走,第一次相交的時候就是那個點
cur1 = cur1.next;
cur2 = cur2.next;
}
return cur1;
}
第三點:如果兩個有環,怎麼求第一個點,以下是三大情況。
第一種:不相交,
第二種:環外相交,
第三種:如你所見,如果遇到了,那麼兩個點都是最近的點,任意返回一個都可以,
如果兩個環的入環節點相等,就是第二個,不是就是1或3。區分1和3,選一個鏈表的入環節點開始next遍歷,如果在再次遇到這個節點之前沒有遇到另一個入環節點就是1,如果相遇了就是3,此時任選一個返回就好了。
做法:直接以入環節點爲終止條件,其本身就是一個無環相交就第一個節點的問題。
public static 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;
}
//情況2,和單鏈表的相交差不多,原先是遇到 null結尾處終止 ,現在是loop處就終止
while (cur2 != loop2) {
n--;
cur2 = cur2.next;
}
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;
} else {//看能不能遇到loop2
cur1 = loop1.next;
while (cur1 != loop1) {//如果都轉回自己,還沒有遇到loop2,就是情況一
if (cur1 == loop2) {
return loop1;
}
cur1 = cur1.next;
}
return null;
}
}