相交链表 图解题解
两条链表可能在某处合为一体——不用哈希,两个指针互换起点就能相遇。
找两条链表的交点,就像两人分别从不同起点出发走向同一个路口:pA 走完 A 就跳到 B 头继续,pB 走完 B 就跳到 A 头继续,各自走了 lenA+lenB 步后,如果有交点两人必然同时到达那个节点——因为绕了一圈后路程相等,误差被抵消;若不相交,两人同时到达 null。
这道题到底在问什么
- 输入
- A=4→1→8→4→5, B=5→6→1→8→4→5
- 输出
- 相交于「值 8」那个节点(共享尾段 8→4→5)
最优解:为什么这么做
一句话答案: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) 空间,只是要多扫一遍、代码更啰嗦——换头写法把「测长对齐」这一步整个省掉了。
▶ 动画逐步走查(共 21 步)——想跟着动画一帧帧对照就展开
- 3思路一句话:pa、pb 各走「自己 + 对方」一整趟,长度差被抹平,刚好在相交点碰头。下面一步步演给你看。
- 4A 共 5 个、B 共 6 个节点(B 长 1 个,这就是长度差)。pa 指 A 头 4,pb 指 B 头 5。
- 5检查起点:pa 指 4(A 头)、pb 指 5(B 头),不是同一节点,要各走一步。
- 6各前移一格:pa → 1,pb → 6。
- 7检查:pa 指 1、pb 指 6——不是同一节点,继续各走一步。
- 8各前移一格:pa → 8,pb → 1。
- 9检查:pa 指 8、pb 指 1——不是同一节点,继续各走一步。
- 10各前移一格:pa → 4,pb → 8。
- 11检查:pa 指 4、pb 指 8——不是同一节点,继续各走一步。
- 12各前移一格:pa → 5,pb → 4。
- 13检查:pa 指 5、pb 指 4——不是同一节点,继续各走一步。
- 14pa 走完了 A、到了尾后 null,按规则跳到 B 的头 5 重新走;pb 继续到 5。
- 15检查:pa 指 5、pb 指 5——不是同一节点,继续各走一步。
- 16pb 走完了 B、到了尾后 null,按规则跳到 A 的头 4 重新走;pa 继续到 6。
- 17检查:pa 指 6、pb 指 4——不是同一节点,继续各走一步。
- 18各前移一格:pa → 1,pb → 1。
- 19检查:pa 指 1、pb 指 1——不是同一节点,继续各走一步。
- 20各前移一格:pa → 8,pb → 8。
- 21检查:pa 和 pb 这一刻都指着「值 8」这个格子——它们是同一个物理节点吗?是!相遇了。
- 22确认相交点 = 「值 8」那个节点。共享尾段 8→4→5 在两行同时标绿,说明 A、B 从这里起共用同一批节点。
- 23结论:相交节点 = 「值 8」那个节点。绿色这一段 8→4→5 是两条链共用的同一批节点;A 独有前缀 4→1,B 独有前缀 5→6→1。
⚠️ 容易写错的地方
✗ 错:按「值相等」判相交
✓ 对:按「节点本身(引用)相等」判
两条链可能有值相同但不是同一节点;相交指物理同一节点
✗ 错:换头条件写成节点的值为空
✓ 对:写成「指针走到 null 就换头」
是指针越过链尾(==null)才换,不是看节点值
✗ 错:不相交时死循环
✓ 对:不相交时两指针同时变 null 后相等退出
pa==pb==null 也满足循环退出条件,自然结束
完整代码(Python / C++ / Java)
Python
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)C++
ListNode* getIntersectionNode(ListNode* headA, ListNode* headB){
ListNode *pa = headA, *pb = headB;
while(pa != pb){ // 比指针(节点)是否相同
pa = pa ? pa->next : headB; // 走到尾换对方头
pb = pb ? pb->next : headA;
}
return pa; // 相遇即相交点(或 nullptr)
}Java
public ListNode getIntersectionNode(ListNode headA, ListNode headB){
ListNode pa = headA, pb = headB;
while(pa != pb){ // 比的是节点引用,不是 val
pa = (pa != null) ? pa.next : headB; // 走到尾换对方头
pb = (pb != null) ? pb.next : headA;
}
return pa; // 相遇即相交点(或 null)
}复杂度
时间
O(m+n)
每个指针至多走 m+n 步
空间
O(1)
只用两个指针,不开额外结构
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 相交链表 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
还有别的解法吗?+
哈希集合:先把 A 所有节点存进 set,再遍历 B 找第一个在 set 里的节点,O(m+n) 时间但 O(m) 空间。双指针更省空间。
为什么是 O(1) 空间却能消除长度差?+
让短链指针先走完自己再走对方、长链同理,两者总步数都是 m+n,到相交点时步数对齐,无需先算长度差。
想听吴师兄把这道题讲给你听?
文字版和动画都随便看。开通图解算法年卡,可以听吴师兄把 相交链表 一步步讲透(全站已上线 905 份讲透,持续扩充), 卡住的地方还有 AI 私教小欧就着动画帮你拆到懂。
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。