Evan Wang
Evan WangFrontEnd Developer
  • Evan Wang
    Evan WangFrontEnd Developer
  • 前端相关
  • 算法题
  • 日常笔记
  • Chat With AI

两数之和
两数相加
无重复字符的最长子串
最长回文子串
盛水最多的容器
三数之和
电话号码的字母组合
有效括号
合并两个有序链表
括号生成
下一个排列
搜索旋转排序数组
在排序数组中查找元素的第一个和最后一个位置
删除链表的倒数第n个节点
组合总和
全排列
旋转图像
字母异位词分组
最大子数组合
跳跃游戏
反转链表
反转链表2
合并区间
最小路径和
编辑距离
颜色分类
爬楼梯
组合
子集
不同的二叉搜索树
验证二叉搜索树
对称二叉树
二叉树的层序遍历
二叉树的最大深度
从前序与中序遍历序列构造二叉树
只出现一次的数字
二叉树展开为链表
最长连续序列
单词拆分
环形链表
环形链表2
排序链表
相交链表
乘积最大子数组
最小栈
多数元素
打家劫舍
数组中重复的数据
二叉树的中序遍历
卖股票的最佳时机
数组中的第K个最大元素

  1. Articles
  1. More
  2. 42-相交链表.mdx
目录
题目思路1代码思路2代码
2489 字
约 7 分钟
更新于 2026/08/20

相交链表

题目

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。
图示两个链表在节点 c1 开始相交: 题目图片1 题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。
自定义评测:
评测系统 的输入如下(你设计的程序 不适用 此输入):
intersectVal - 相交的起始节点的值。如果不存在相交节点,这一值为 0
listA - 第一个链表
listB - 第二个链表
skipA - 在 listA 中(从头节点开始)跳到交叉节点的节点数
skipB - 在 listB 中(从头节点开始)跳到交叉节点的节点数
评测系统将根据这些输入创建链式数据结构,并将两个头节点 headA 和 headB 传递给你的程序。如果程序能够正确返回相交节点,那么你的解决方案将被 视作正确答案 。

示例 1:
示例1

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at '8'
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,6,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
— 请注意相交节点的值不为 1,因为在链表 A 和链表 B 之中值为 1 的节点 (A 中第二个节点和 B 中第三个节点) 是不同的节点。换句话说,它们在内存中指向两个不同的位置,而链表 A 和链表 B 中值为 8 的节点 (A 中第三个节点,B 中第四个节点) 在内存中指向相同的位置。

示例2
示例2

输入:intersectVal = 2, listA = [1,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Intersected at '2'
解释:相交节点的值为 2 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [1,9,1,2,4],链表 B 为 [3,2,4]。
在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。

示例3
示例3

输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。
由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
这两个链表不相交,因此返回 null 。

思路1

最简单的方法,使用 Map 结构存储一条链表上的节点,然后遍历另一条,看在 Map 中是否能找到相同的节点。 如果找到即相交,找不到即没有相交。

代码

var getIntersectionNode = function(headA, headB) {
    const map = new Map()
    while(headA) {
        map.set(headA, headA)
        headA = headA.next;
    }
 
    while(headB) {
        if(map.has(headB)) {
            return headB;
        }
        headB = headB.next;
    }
 
    return null
};

思路2

刚开始,当 headA 或者 headB 有一个为空的时候,说明两个链表不相交,直接返回 null
声明两个指针 hA 和 hB, 分别只想 headA 和 headB, 然后在 while 循环里面同时遍历 headA 和 headB
由于两个链表长度不一样,所以必然会有一个指针先为 null

  • 当 hA 为 null 时,将 hA 指向 headB;
  • 当 hB 为 null 时,将 hB 指向 headA; 继续遍历
  • 当 hA === hB 时,说明两条链表相交,结束循环
  • 当 hA === null && hB === null 时候,说明两条链表不相交,结束循环。

因为两条链表长度如果不一样(headA, headB, 假设 A.length 大于 B.length ),同时开始遍历,必然是 hB 先遍历完 headB,
hB 遍历完 headB 后转去遍历 headA,
后面当 hA 遍历完 headA,转去遍历 headB 时,
hB 此时已经多遍历了 headA.length - headB.length
后面两个指针的遍历速度,相对于相交点,就可以看作是同步的位置了。

代码

var getIntersectionNode = function (headA, headB) {
    if(!headA || !headB) return null
    let hA = headA;
    let hB = headB;
 
    while(hA || hB) { // 当 hA 或者 hB 不为空时,遍历继续。
        if(hA === hB) {
            return hA
        }
        if(hA === null) {
            hA = headB;
        } else {
            hA = hA.next;
        }
        
        if(hB === null) {
            hB = headA;
        } else {
            hB = hB.next;
        }
        
    }
 
    return null
};