注:此博客不再更新,所有最新文章將發表在個人獨立博客limengting.site。分享技術,記錄生活,歡迎大家關注
題目描述
輸入兩個鏈表,找出它們的第一個公共結點。
根據提交結果來看,只需要考慮無環單鏈表:
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
if (pHead1 == null || pHead2 == null) return null;
// 空間複雜度O(1)的方法:
// 先遍歷得到鏈表一和鏈表二的長度和最後一個節點,如果end1 != end2則不可能相交
// end1 == end2,若鏈表1長度爲100,鏈表2長度爲80,則head1先走20,然後head1和head2一起走,相遇處即第一個相交節點處
ListNode cur = pHead1;
int count = 0;
while (cur.next != null) {
cur = cur.next;
count++;
}
ListNode end1 = cur;
cur = pHead2;
while (cur.next != null) {
cur = cur.next;
count--;
}
ListNode end2 = cur;
if (end1 != end2) return null;
ListNode longer = count > 0 ? pHead1 : pHead2;
ListNode shorter = longer == pHead1 ? pHead2 : pHead1;
count = Math.abs(count);
while (count > 0) {
longer = longer.next;
count--;
}
while (longer != shorter) {
longer = longer.next;
shorter = shorter.next;
}
return longer;
}
}
若考慮鏈表有環和無環的情況:
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
// 2、O(1),準備兩個指針,快指針一次走兩步,慢指針一次走一步
// 如果快指針遇到空,返回無環
// 如果快指針和慢指針相遇,快指針回到開頭,以後快指針和慢指針都一次走一步,快指針和慢指針會在入環節點處相遇
// 2、不使用輔助空間
// 快指針一次走兩步,慢指針一次走一步,如果快指針遇到空則返回無環
// 如果有環則快指針和慢指針一定會相遇,相遇後快指針回到開頭,以後快指針和慢指針都一次走一步,相遇處即爲入環節點
public static ListNode getLoopNode2(ListNode head) {
if (head == null) return null;
ListNode fast = head.next.next;
ListNode slow = head.next;
while (fast != slow) {
if (fast == null || fast.next == null) {
return null;
}
slow = slow.next;
fast = fast.next.next;
}
fast = head;
while (fast != slow) {
slow = slow.next;
fast = fast.next;
}
return fast;
}
// 找到第一個相交的節點
// 1、判斷兩個無環單鏈表是否相交:
// (1) O(n):鏈表一的節點都放到map裏,遍歷鏈表二,第一個在map中的節點爲相交節點,如果遍歷到空都沒找到在map中的則不相交
// (2) O(1):先遍歷鏈表一和鏈表二,統計鏈表一、二的長度和鏈表一、二的最後一個節點
// 如果end1 != end2,鏈表一和鏈表二不可能相交
// 如果end1 == end2,長度一100和長度二80比較,head1先走20步,然後head1和head2一起走,他們一定會共同走到第一個相交的節點處
// 2、一個有環一個無環的單鏈表,不可能相交
// 3、兩個有環的單鏈表:三種情況
public static ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
if (pHead1 == null || pHead2 == null) return null;
// loop1爲鏈表1的入環節點
// loop2爲鏈表2的入環節點
ListNode loop1 = getLoopNode2(pHead1);
ListNode loop2 = getLoopNode2(pHead2);
// 兩個鏈表都無環
if (loop1 == null && loop2 == null) {
return noLoop2(pHead1, pHead2);
}
if (loop1 != null && loop2 != null) {
return bothLoop(pHead1, pHead2, loop1, loop2);
}
return null;
}
private static ListNode noLoop2(ListNode pHead1, ListNode pHead2) {
// 空間複雜度O(1)的方法:
// 先遍歷得到鏈表一和鏈表二的長度和最後一個節點,如果end1 != end2則不可能相交
// end1 == end2,若鏈表1長度爲100,鏈表2長度爲80,則head1先走20,然後head1和head2一起走,相遇處即第一個相交節點處
ListNode cur = pHead1;
int count = 0;
while (cur.next != null) {
cur = cur.next;
count++;
}
ListNode end1 = cur;
cur = pHead2;
while (cur.next != null) {
cur = cur.next;
count--;
}
ListNode end2 = cur;
if (end1 != end2) return null;
ListNode longer = count > 0 ? pHead1 : pHead2;
ListNode shorter = longer == pHead1 ? pHead2 : pHead1;
count = Math.abs(count);
while (count > 0) {
longer = longer.next;
count--;
}
while (longer != shorter) {
longer = longer.next;
shorter = shorter.next;
}
return longer;
}
private static ListNode bothLoop(ListNode pHead1, ListNode pHead2, ListNode loop1, ListNode loop2) {
if (loop1 == loop2) {
return bothLoopCase2(pHead1, pHead2, loop1);
}
ListNode cur1 = loop1;
while (cur1 != null) {
if (cur1 == loop2) {
return loop1; // case3
}
cur1 = cur1.next;
}
return null; // case1
}
// 此情況下等同於無環鏈表相交
private static ListNode bothLoopCase2(ListNode pHead1, ListNode pHead2, ListNode loop) {
ListNode cur = pHead1;
int count = 0;
while (cur.next != loop) {
cur = cur.next;
count++;
}
ListNode end1 = cur;
cur = pHead2;
while (cur.next != loop) {
cur = cur.next;
count--;
}
ListNode end2 = cur;
if (end1 != end2) return null;
ListNode longer = count > 0 ? pHead1 : pHead2;
ListNode shorter = longer == pHead1 ? pHead2 : pHead1;
count = Math.abs(count);
while (count > 0) {
longer = longer.next;
count--;
}
while (longer != shorter) {
longer = longer.next;
shorter = shorter.next;
}
return longer;
}
}