目录
1207
6 分钟
判断两个单链表是否相交并找出交点:双指针交替遍历法

题目描述#

给定两个单链表的头节点 headAheadB,判断它们是否相交(即某个节点被两个链表共享)。如果相交,返回交点;否则返回 null

输入:headA = 4→1→8→4→5
      headB = 5→6→1→8→4→5

                    交点
输出:节点 8

两条链表在节点 8 处交汇,之后共享 4→5 部分。

解题思路#

暴力法的问题#

  • 哈希表法:遍历链表 A,将所有节点存入哈希表;再遍历链表 B,检查每个节点是否在哈希表中。空间 O(n)。
  • 双指针交替遍历法:空间 O(1),一次遍历完成。

双指针交替遍历法#

关键观察:如果两条链表有交点,那么交点之后的节点完全相同。两链表长度可能不同,但从各自头节点走到交点再走到末尾的总路径长度是固定的

核心思想#

让两个指针分别从 A 和 B 出发:

  • 指针 pA:从 A 的头出发,走到末尾后跳到 B 的头继续走
  • 指针 pB:从 B 的头出发,走到末尾后跳到 A 的头继续走

如果存在交点,两个指针走过的总长度相等(都是 lenA + lenB),它们会在交点相遇。如果不存在交点,两个指针最终同时到达 null

数学证明#

设链表 A 的长度为 lenA = a + c(a 为 A 独有部分,c 为共享部分),链表 B 的长度为 lenB = b + c

指针第一段第二段总距离到达交点
pAac + ba + c + b
pBbc + ab + c + a

两个指针走过的总距离都是 a + b + c,所以会同时到达交点(或同时到达 null)。

算法步骤#

  1. pA = headA, pB = headB
  2. 循环条件:while (pA != pB)
  3. pA 走一步;若 pA == null,跳到 headB
  4. pB 走一步;若 pB == null,跳到 headA
  5. 循环结束,返回 pA(交点或 null)

代码实现#

C
#include <stdio.h>
#include <stdlib.h>

typedef struct ListNode {
    int val;
    struct ListNode *next;
} ListNode;

ListNode* getIntersectionNode(ListNode *headA, ListNode *headB) {
    if (!headA || !headB) return NULL;

    ListNode *pA = headA;
    ListNode *pB = headB;

    // 两个指针交替遍历
    while (pA != pB) {
        pA = pA ? pA->next : headB; // pA 走完A走B
        pB = pB ? pB->next : headA; // pB 走完B走A
    }

    // 相遇点:交点或 null
    return pA;
}
C++
struct ListNode {
    int val;
    ListNode *next;
    ListNode(int x) : val(x), next(nullptr) {}
};

class Solution {
public:
    ListNode* getIntersectionNode(ListNode *headA, ListNode *headB) {
        if (!headA || !headB) return nullptr;

        ListNode *pA = headA;
        ListNode *pB = headB;

        // 两个指针交替遍历
        while (pA != pB) {
            pA = pA ? pA->next : headB; // pA 走完A走B
            pB = pB ? pB->next : headA; // pB 走完B走A
        }

        // 相遇点:交点或 nullptr
        return pA;
    }
};
JavaScript
class ListNode {
    constructor(val) {
        this.val = val;
        this.next = null;
    }
}

function getIntersectionNode(headA, headB) {
    if (!headA || !headB) return null;

    let pA = headA;
    let pB = headB;

    // 两个指针交替遍历
    while (pA !== pB) {
        pA = pA ? pA.next : headB; // pA 走完A走B
        pB = pB ? pB.next : headA; // pB 走完B走A
    }

    // 相遇点:交点或 null
    return pA;
}

图解过程#

有交点的情况#

链表A:  a1 → a2

                   c1 → c2 → c3

链表B:  b1 → b2 → b3
pA 路径:a1 → a2 → c1 → c2 → c3 → b1 → b2 → b3 → ★c1
pB 路径:b1 → b2 → b3 → c1 → c2 → c3 → a1 → a2 → ★c1

★ 处两指针相遇,c1 就是交点 ✓

无交点的情况#

链表A:  1 → 2 → 3
链表B:  4 → 5
pA 路径:1 → 2 → 3 → 4 → 5 → null
pB 路径:4 → 5 → 1 → 2 → 3 → null

★ 两指针同时到达 null ✓

复杂度分析#

维度分析
时间复杂度O(m + n) — 两指针各走 m + n 步
空间复杂度O(1) — 只用了两个指针

关键要点#

  1. “走你走过的路”:pA 走完自己走 B,pB 走完自己走 A——这个设计让两指针消除长度差,同时到达交点
  2. 同时到达 null:如果没有交点,两个指针也会在遍历相同总长度后同时到达 null,循环自然结束
  3. 不能中途跳过 null:有些写法是 “走到 null 就跳”,这没问题,因为无环链表最后都是 null
  4. 与环检测的区别:本题假设链表无环。如果可能有环,需要先判环

扩展思考#

  • 如果链表可能含有环,如何判断相交?—— 需要分情况讨论:都无环、一个有环一个无环、都有环
  • 是否存在更直观的解法?—— 可以先算出两链表长度差,让较长链表的指针先走”差”步,然后同步走

相关文章:

判断两个单链表是否相交并找出交点:双指针交替遍历法
https://www.hehonglei.cn/posts/linked-list-intersection/
作者
Honglei He
发布于
2026-08-10
许可协议
CC BY-NC-SA 4.0