返回

剖析相交链表:LeetCode 160 的巧妙解法

前端

在编程世界里,LeetCode 犹如一座代码试炼场,其中第 160 题——相交链表,以其独特的解法吸引着无数开发者。这道看似简单的题目,却蕴含着算法设计的精髓。本文将带你深入理解这道题,并解析其背后的巧妙思路,助你提升算法思维能力。

当我们面对两条可能相交的链表时,一个直接的想法是遍历每个链表,并将节点存储在一个集合中。然后,遍历另一个链表,检查每个节点是否在集合中出现过。如果出现,则说明找到了相交节点。这种方法虽然简单易懂,但需要额外的空间来存储节点。有没有更优雅、更节省空间的解决方案呢?

答案是肯定的,那就是 双指针法 。这种方法的核心思想是利用两个指针,分别从两个链表的头节点出发,以相同的速度遍历链表。当其中一个指针到达链表尾部时,将其指向另一个链表的头节点,继续遍历。当另一个指针也到达链表尾部时,将其指向第一个链表的头节点,继续遍历。如果两个链表存在相交节点,那么这两个指针最终会在相交节点相遇。

为什么这种方法能够找到相交节点呢?我们可以想象一下,如果两个链表相交,那么从相交节点到链表尾部的部分是相同的。当两个指针分别遍历完各自的链表后,它们相当于都走过了各自链表的长度,再加上从相交节点到链表尾部的长度。因此,它们最终会在相交节点相遇。

为了更好地理解双指针法,我们来看一个具体的例子。假设有两个链表 A 和 B,它们的结构如下:

A: 1 -> 2 -> 3 -> 4 -> 5
B: 6 -> 7 -> 4 -> 5

相交节点是 4。

我们使用两个指针 ptrA 和 ptrB,分别指向链表 A 和 B 的头节点。

  1. ptrA 指向 1,ptrB 指向 6。
  2. ptrA 指向 2,ptrB 指向 7。
  3. ptrA 指向 3,ptrB 指向 4。
  4. ptrA 指向 4,ptrB 指向 5。
  5. ptrA 指向 5,ptrB 指向 null。
  6. ptrB 指向 1,ptrA 指向 null。
  7. ptrA 指向 6,ptrB 指向 2。
  8. ptrA 指向 7,ptrB 指向 3。
  9. ptrA 指向 4,ptrB 指向 4。

此时,ptrA 和 ptrB 都指向了相交节点 4,算法结束。

下面是用 Python 实现双指针法的代码:

class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

def getIntersectionNode(headA: ListNode, headB: ListNode) -> ListNode:
    if headA is None or headB is None:
        return None

    ptrA = headA
    ptrB = headB

    while ptrA != ptrB:
        ptrA = headB if ptrA is None else ptrA.next
        ptrB = headA if ptrB is None else ptrB.next

    return ptrA

双指针法不仅思路巧妙,而且代码简洁易懂,时间复杂度为 O(m+n),空间复杂度为 O(1),其中 m 和 n 分别是两个链表的长度。

常见问题解答

1. 双指针法一定能找到相交节点吗?

是的,如果两个链表存在相交节点,那么双指针法一定能找到它。

2. 如果两个链表不相交,双指针法会发生什么?

如果两个链表不相交,那么两个指针最终都会指向 null,算法会返回 null。

3. 双指针法的效率如何?

双指针法的时间复杂度为 O(m+n),空间复杂度为 O(1),是一种非常高效的算法。

4. 双指针法还有哪些应用场景?

除了检测链表是否相交,双指针法还可以用于检测链表中是否存在环,以及寻找链表的中间节点等。

5. 如何学习更多关于双指针法的知识?

可以通过阅读算法书籍、观看算法视频教程,以及练习 LeetCode 上的相关题目来学习更多关于双指针法的知识。

希望本文能够帮助你理解 LeetCode 160 相交链表这道题,并掌握双指针法的应用。在算法学习的道路上,不断探索和实践,才能提升自己的算法思维能力。