返回

如影随形,寻觅双链交点:LeetCode 160 相交链表算法之美

闲谈

在算法竞赛和编程实践中,链表数据结构的使用可谓随处可见,解决各种复杂问题时,它总是能展现出独到的优势。LeetCode 160 相交链表便是这样一个极具代表性的问题,它要求我们寻找到两个单链表的相交起始节点,为算法工程师们提供了绝佳的挑战。

算法问题
给出两个单链表的头节点 headA 和 headB ,找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。

理解题意:
题目要求我们找到两个单链表的相交起始节点,关键在于理解相交起始节点的概念。两个单链表的相交起始节点是指两个链表中第一个相同的节点,它是两个链表共同拥有的最长公共后缀。如果两个链表没有交点,则不存在相交起始节点,需要返回 null。

算法思路:
解决此问题,我们可以采用双指针法。

第一步,设置两个指针,分别指向链表 headA 和 headB 的头节点。
第二步,让两个指针同时向前移动,每移动一步,就将指针指向下一个节点。
第三步,当其中一个指针到达链表末尾时,将其重置为另一个链表的头节点。
第四步,重复步骤二和步骤三,直到两个指针相遇或都到达链表末尾。
第五步,如果两个指针相遇,则相遇点就是相交起始节点;如果两个指针都到达链表末尾,则两个链表没有交点,返回 null。

算法流程:

  1. 初始化两个指针,分别指向链表 headA 和 headB 的头节点。
  2. while (headA != null && headB != null) {
  • if (headA == headB) {
    • return headA; // 两个指针相遇,说明找到交点。
      }
  • headA = headA.next;
  • headB = headB.next;
    }
  1. if (headA == null) {
  • headA = headB; // headA 指针指向链表 headB 的头节点。
    } else {
  • headB = headA; // headB 指针指向链表 headA 的头节点。
    }
  1. while (headA != null) {
  • if (headA == headB) {
    • return headA; // 两个指针相遇,说明找到交点。
      }
  • headA = headA.next;
  • headB = headB.next;
    }
  1. return null; // 两个指针都到达链表末尾,说明没有交点。

复杂度分析:

时间复杂度:O(m + n),其中 m 和 n 分别是链表 headA 和 headB 的长度。
空间复杂度:O(1),因为我们只需要两个指针,不需要额外的空间。

实现代码:

public class Solution {
  public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
    // 初始化两个指针,分别指向链表 headA 和 headB 的头节点。
    ListNode pA = headA;
    ListNode pB = headB;

    // 让两个指针同时向前移动,每移动一步,就将指针指向下一个节点。
    while (pA != null && pB != null) {
      // 如果两个指针相遇,则相遇点就是相交起始节点。
      if (pA == pB) {
        return pA;
      }
      // 如果其中一个指针到达链表末尾,则将其重置为另一个链表的头节点。
      if (pA.next == null) {
        pA = headB;
      } else {
        pA = pA.next;
      }
      if (pB.next == null) {
        pB = headA;
      } else {
        pB = pB.next;
      }
    }

    // 如果两个指针都到达链表末尾,则两个链表没有交点,返回 null。
    return null;
  }
}

算法要点:

  • 双指针法是解决此问题最为简洁有效的方法之一。
  • 指针移动过程中,当其中一个指针到达链表末尾时,将其重置为另一个链表的头节点,巧妙地实现了两个链表的交替遍历。
  • 算法的核心思想在于,如果两个链表相交,那么它们的相交起始节点必然是两个指针相遇的节点。

除了双指针法,还有其他解决方法,例如哈希表法和栈法。

哈希表法:将其中一个链表的所有节点放入哈希表,然后遍历另一个链表,对于每个节点,检查其是否存在于哈希表中,如果存在,则说明找到了相交起始节点。

栈法:将两个链表的节点依次入栈,然后逐个出栈比较,如果两个节点相等,则说明找到了相交起始节点。

不同的方法各有其优劣,双指针法在时间和空间复杂度上都具有较好的表现,哈希表法在链表长度较长时性能较好,栈法在链表长度较短时性能较好。