跳转至

JZ52 两个链表的一个公共结点

题目描述

image-20231124173953184

解题思路

设 A 的长度为 a + c,B 的长度为 b + c,其中 c 为尾部公共部分长度,可知 a + c + b = b + c + a。

当访问链表 A 的指针访问到链表尾部时,令它从链表 B 的头部重新开始访问链表 B;同样地,当访问链表 B 的指针访问到链表尾部时,令它从链表 A 的头部重新开始访问链表 A。这样就能控制访问 A 和 B 两个链表的指针能同时访问到交点。

public class Solution {

  public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
    // 处理空的情况
    if (pHead1 == null || pHead2 == null) {
      return null;
    }
    // 判断是否有环,并且返回环的入口地址
    ListNode loop1 = EntryNodeOfLoop(pHead1);
    ListNode loop2 = EntryNodeOfLoop(pHead2);
    // 必须都有环或者无环
    // 若一个有环,另一个无环,则返回 null
    if (loop1 == null ^ loop2 == null) {
      return null;
    }
    // 三种情况:1. 同一个入口 2. 不同入口 3. 完全是两个环
    // case 1 同一个入口地址,将入口作为结尾,计算无环情况
    if (loop1 == loop2) {
      return FirstCommonNodeBeforeLoop(pHead1, pHead2, loop1);
    }
    // case 2 不同入口,其中一个增加,必然在某个位置相交
    // 如果循环一圈之后还不相交,说明是 case 3
    ListNode tempNode = loop1;
    do{
      tempNode = tempNode.next;
      if(tempNode == loop2) {
        return loop1;
      }
    } while(tempNode != loop1);
    return null;
  }

  public static ListNode FirstCommonNodeBeforeLoop(ListNode pHead1, ListNode pHead2, ListNode endNode) {
    ListNode curNode1 = pHead1;
    ListNode curNode2 = pHead2;
    // 算 pHead1 长度
    int len1 = 0;
    while (curNode1 != endNode) {
      len1++;
      curNode1 = curNode1.next;
    }
    // 算 pHead2 长度
    int len2 = 0;
    while (curNode2 != endNode) {
      len2++;
      curNode2 = curNode2.next;
    }
    // 快慢指针法
    curNode1 = pHead1;
    curNode2 = pHead2;
    if (len1 >= len2) {
      for (int i = 0; i < len1 - len2; i++) {
        curNode1 = curNode1.next;
      }
    } else {
      for (int i = 0; i < len2 - len1; i++) {
        curNode2 = curNode2.next;
      }
    }
    while (curNode1 != curNode2) {
      curNode1 = curNode1.next;
      curNode2 = curNode2.next;
    }
    return curNode1;
  }

  public static ListNode EntryNodeOfLoop(ListNode pHead) {
    ListNode fastPointer = pHead;
    ListNode slowPointer = pHead;
    do {
      if (fastPointer.next == null || fastPointer.next.next == null) {
        return null;
      }
      // 快慢指针法
      fastPointer = fastPointer.next.next;
      slowPointer = slowPointer.next;
    } while (fastPointer != slowPointer);
    // 寻找交点
    fastPointer = pHead;
    while (fastPointer != slowPointer) {
      fastPointer = fastPointer.next;
      slowPointer = slowPointer.next;
    }
    return fastPointer;
  }
}