题目描述
思路解析
一句话答案:LeetCode 160 相交链表的最优解是双指针换头:pa 走完链 A 就转到链 B 的头继续走,pb 走完 B 就转到 A,两人总路程同为 m+n,长度差被自动抹平,第一次指向同一节点的位置就是相交起点;不相交则同时走到 null 退出。O(m+n) 时间、O(1) 空间。
相交链表比的是节点还是值
两条单链表如果相交,由于每个节点只有一个 next,从相交点开始它们共用同一段尾巴,整体呈 Y 字形而不是 X 形。题目要找的是「开始相交的那个节点」,判断标准是节点本身——同一块内存、同一个引用,而不是值相等;两条链完全可能各自带着值相同却互不相干的节点。这个区别决定了代码里必须比较引用,比较 val 会误判。
如果两条链一样长,问题有多简单
先假设两条链长度相同:让两个指针从各自头部同步各走一步,它们距离相交点的剩余步数时刻相等,必然在相交点第一次碰头。真正的麻烦只有一个——长度不同时两个指针错了位,同步走永远差着固定几步。于是整道题归结为:怎么在不预先测长度的前提下,把这个长度差抹掉。
换头为什么恰好抹平长度差
设链 A 独有部分长 a、链 B 独有部分长 b、公共尾段长 c。让 pa 走完链 A 后接着从链 B 的头继续走,pb 走完链 B 后接着从链 A 的头继续走。pa 到达相交点的总步数是 a+c+b,pb 是 b+c+a,两个式子完全相等。
也就是说,两个指针各自把「自己的路加对方的路」走一遍之后,步调必然对齐,第一次同时踩在同一个节点上时,那里就是相交起点。长度差没有被显式计算,而是被交换路径这件事天然抵消了——这正是这个解法优雅的地方。
不相交的时候为什么不会死循环
如果两条链不相交,可以把公共段长度 c 看成零:pa 走完 a+b 步、pb 走完 b+a 步之后,两者同时越过链尾变成 null。null 等于 null,循环条件「pa 不等于 pb」不再成立,函数返回 null,恰好是不相交时的正确答案,不需要任何特判。
前提是换头时机要写对:是指针走到 null 之后才跳到对方链头,而不是走到最后一个节点就跳。跳早了两个指针的总路程不再相等,会永远错位,真的陷入死循环。
复杂度与其他解法的取舍
双指针每人最多走 m+n 步,时间 O(m+n),只用两个指针变量,空间 O(1)。替代方案是哈希集合:先把链 A 的所有节点存进 set,再顺着链 B 找第一个出现在 set 里的节点,时间同为 O(m+n),但要 O(m) 额外空间。也可以先各自测长、让长链先走掉差值再同步走,同样 O(1) 空间,只是要多扫一遍、代码更啰嗦——换头写法把「测长对齐」这一步整个省掉了。
▶ 动画逐步走查(文字版)——想跟着上方动画一帧帧对照就展开
思路一句话:pa、pb 各走「自己 + 对方」一整趟,长度差被抹平,刚好在相交点碰头。下面一步步演给你看。
A 共 5 个、B 共 6 个节点(B 长 1 个,这就是长度差)。pa 指 A 头 4,pb 指 B 头 5。
检查起点:pa 指 4(A 头)、pb 指 5(B 头),不是同一节点,要各走一步。
各前移一格:pa → 1,pb → 6。
检查:pa 指 1、pb 指 6——不是同一节点,继续各走一步。
各前移一格:pa → 8,pb → 1。
检查:pa 指 8、pb 指 1——不是同一节点,继续各走一步。
各前移一格:pa → 4,pb → 8。
检查:pa 指 4、pb 指 8——不是同一节点,继续各走一步。
各前移一格:pa → 5,pb → 4。
检查:pa 指 5、pb 指 4——不是同一节点,继续各走一步。
pa 走完了 A、到了尾后 null,按规则跳到 B 的头 5 重新走;pb 继续到 5。
检查:pa 指 5、pb 指 5——不是同一节点,继续各走一步。
pb 走完了 B、到了尾后 null,按规则跳到 A 的头 4 重新走;pa 继续到 6。
检查:pa 指 6、pb 指 4——不是同一节点,继续各走一步。
各前移一格:pa → 1,pb → 1。
检查:pa 指 1、pb 指 1——不是同一节点,继续各走一步。
各前移一格:pa → 8,pb → 8。
检查:pa 和 pb 这一刻都指着「值 8」这个格子——它们是同一个物理节点吗?是!相遇了。
确认相交点 = 「值 8」那个节点。共享尾段 8→4→5 在两行同时标绿,说明 A、B 从这里起共用同一批节点。
结论:相交节点 = 「值 8」那个节点。绿色这一段 8→4→5 是两条链共用的同一批节点;A 独有前缀 4→1,B 独有前缀 5→6→1。
不相交与空链都靠「同时到 null」自然收口。
哈希解法与「为什么 O(1)」是两个高频追问。
参考代码
def getIntersectionNode(headA, headB): pa, pb = headA, headB while pa is not pb: # 比的是节点本身,不是值 pa = pa.next if pa else headB # 走到尾就换到对方链头 pb = pb.next if pb else headA return pa # 相遇即相交点(或同为 None)复杂度
- 时间:O(m+n),每个指针至多走 m+n 步
- 空间:O(1),只用两个指针,不开额外结构
易错点
面试追问把动画讲成自己的话
追问还有别的解法吗?
追问为什么是 O(1) 空间却能消除长度差?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
回文链表
LeetCode 234 · 简单 · 沿着 链表 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题