news 2026/8/18 5:50:27

相交链表问题的双指针解法与优化策略

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
相交链表问题的双指针解法与优化策略

1. 相交链表问题概述

相交链表是链表类题目中的经典问题,题目编号160。给定两个单链表的头节点 headA 和 headB,要求找出并返回两个单链表相交的起始节点。如果两个链表没有交点,则返回 null。

这个问题的难点在于:

  • 两个链表可能在相交前有不同长度
  • 需要设计时间复杂度为 O(m+n)、空间复杂度为 O(1) 的算法
  • 需要处理各种边界情况(如一个链表为空、两个链表都为空等)

2. 暴力解法与哈希表法

2.1 暴力解法分析

最直观的解法是双重循环遍历:

def getIntersectionNode(headA, headB): pA = headA while pA: pB = headB while pB: if pA == pB: return pA pB = pB.next pA = pA.next return None

时间复杂度:O(mn) 空间复杂度:O(1)

这种解法虽然简单,但在力扣上会因超时无法通过所有测试用例。

2.2 哈希表法优化

使用哈希集合存储访问过的节点:

def getIntersectionNode(headA, headB): visited = set() pA = headA while pA: visited.add(pA) pA = pA.next pB = headB while pB: if pB in visited: return pB pB = pB.next return None

时间复杂度:O(m+n) 空间复杂度:O(m)或O(n)

虽然时间复杂度达标,但空间复杂度不符合O(1)的要求。

3. 双指针最优解法

3.1 算法思路

最优解法使用双指针,核心思想是:

  1. 指针pA从headA开始遍历,pB从headB开始遍历
  2. 当pA到达链表末尾时,重定位到headB
  3. 当pB到达链表末尾时,重定位到headA
  4. 当pA和pB相遇时,即为相交节点

3.2 数学证明

设:

  • 链表A不相交部分长度为a
  • 链表B不相交部分长度为b
  • 相交部分长度为c

则:

  • pA走过的路径:a + c + b
  • pB走过的路径:b + c + a 两者必然在相交点相遇(c≥0)或同时到达末尾(c=0)

3.3 代码实现

def getIntersectionNode(headA, headB): if not headA or not headB: return None pA, pB = headA, headB while pA != pB: pA = pA.next if pA else headB pB = pB.next if pB else headA return pA

时间复杂度:O(m+n) 空间复杂度:O(1)

4. 边界条件与测试用例

4.1 常见边界情况

  1. 两个链表都为空
  2. 一个链表为空
  3. 两个链表不相交
  4. 两个链表完全重合
  5. 相交点在第一个节点
  6. 相交点在最后一个节点

4.2 测试用例示例

# 用例1:不相交 # 1->2->3 # 4->5 assert getIntersectionNode(headA1, headB1) == None # 用例2:在中间相交 # 1->2->3 # ↘ # 4->5 # ↗ # 6 assert getIntersectionNode(headA2, headB2).val == 4 # 用例3:一个链表为空 assert getIntersectionNode(headA3, None) == None

5. 实际应用与变种问题

5.1 实际应用场景

  1. 内存管理:检测两个对象引用是否指向同一内存区域
  2. 社交网络:查找两个用户的共同好友
  3. 版本控制:查找两个分支的最近共同祖先

5.2 相关变种题目

  1. 环形链表(LeetCode 141)
  2. 环形链表II(LeetCode 142)
  3. 合并两个有序链表(LeetCode 21)
  4. 反转链表(LeetCode 206)

6. 性能优化与注意事项

6.1 优化技巧

  1. 先计算两个链表长度,让长链表指针先走差值步
  2. 使用位运算优化指针比较
  3. 在内存受限环境下,可以考虑牺牲时间换空间

6.2 常见错误

  1. 忘记处理链表为空的情况
  2. 循环终止条件设置错误导致死循环
  3. 指针移动顺序错误
  4. 误认为节点值相同就是相交点(实际应比较节点对象)

7. 不同语言实现对比

7.1 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; }

7.2 Java实现

public ListNode getIntersectionNode(ListNode headA, ListNode headB) { ListNode pA = headA, pB = headB; while (pA != pB) { pA = (pA != null) ? pA.next : headB; pB = (pB != null) ? pB.next : headA; } return pA; }

7.3 JavaScript实现

var getIntersectionNode = function(headA, headB) { let pA = headA, pB = headB; while (pA !== pB) { pA = pA ? pA.next : headB; pB = pB ? pB.next : headA; } return pA; };

8. 链表问题解题通用思路

  1. 画图辅助理解:可视化链表结构
  2. 考虑双指针:快慢指针、前后指针等
  3. 递归与迭代:根据场景选择合适方法
  4. 虚拟头节点:简化边界条件处理
  5. 反转链表:某些问题的关键步骤

对于相交链表问题,我个人的经验是:一定要先在纸上画出各种可能的情况,包括相交和不相交的场景,以及各种边界情况。双指针解法虽然巧妙,但如果不理解其背后的数学原理,很容易在面试中被问倒。在实际编码时,要特别注意指针移动的顺序和终止条件,这些都是容易出错的地方。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/18 5:50:25

DolphinDB时序数据库核心技术解析与工业应用实践

1. 为什么DolphinDB能稳居时序数据库榜首? 时序数据库赛道近年来竞争异常激烈,但DolphinDB却能在众多选手中脱颖而出,这背后有几个关键的技术突破点。首先是它的混合存储引擎设计,将列式存储与内存计算完美结合,针对工…

作者头像 李华
网站建设 2026/8/18 5:49:30

老主板通过Clover引导实现NVMe SSD系统盘安装与优化指南

1. 项目概述:让老主板焕发第二春手头有一块老主板,看着它支持的SATA接口固态硬盘,再看看现在白菜价的NVMe SSD,心里总有点痒痒。NVMe协议带来的速度飞跃是实实在在的,但老主板原生不支持NVMe引导,直接装上只…

作者头像 李华
网站建设 2026/8/18 5:49:25

从AI玩具到生产工具:工程化思维构建稳健个人智能体

1. 从“玩具”到“工具”:个人AI智能体的工程化之痛最近和几个做AI应用的朋友聊天,大家都有一个共同的感受:用各种大模型API或者开源框架,快速搭一个能对话、能执行简单任务的“智能体”(Agent)demo&#x…

作者头像 李华
网站建设 2026/8/18 5:48:44

嵌入式软件工程师核心能力与实战指南:从硬件驱动到系统架构

1. 项目概述:从“拧螺丝”到“造大脑”的跨界玩家“嵌入式软件工程师是干啥的?” 这个问题,我入行前也问过,入行后更是被亲朋好友问了无数遍。简单来说,我们就是给那些“不会说话”的智能硬件写“灵魂”代码的人。你每…

作者头像 李华
网站建设 2026/8/18 5:46:42

深入解析printf性能瓶颈:从原理到实战的嵌入式与高性能计算优化策略

1. 项目概述:为什么我们还在乎printf的性能?在嵌入式开发、高性能计算、游戏引擎或者任何对延迟和吞吐量有极致要求的场景里,你可能会觉得printf这种“古老”的库函数调用,早该被更现代的日志库或序列化方案取代了。但现实是&…

作者头像 李华