注:此博客不再更新,所有最新文章将发表在个人独立博客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;
}
}