news 2026/11/4 17:45:22

LeetCode 445题:栈与头插法解决链表相加问题

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LeetCode 445题:栈与头插法解决链表相加问题

1. 题目解析与核心挑战

LeetCode 445题"两数相加II"要求我们对两个非空链表表示的非负整数进行相加,其中数字的最高位位于链表头部。这与常规的链表相加问题(LeetCode 2题)形成鲜明对比,后者数字的低位在链表头部。

核心难点在于:

  • 链表只能单向遍历,无法直接反向访问
  • 加法运算需要从最低位开始对齐计算
  • 需要考虑进位处理
  • 结果链表仍需保持高位在前的顺序

2. 解题思路与算法设计

2.1 栈结构的巧妙运用

解决这个问题的关键在于如何实现从低位到高位的访问顺序。栈(Stack)的先进后出(FILO)特性完美匹配这一需求:

  1. 遍历两个链表,将所有节点值分别压入两个栈
  2. 此时栈顶元素即为数字的最低位
  3. 通过反复出栈操作,实现从低位到高位的访问
stack<int> s1, s2; while (l1) { s1.push(l1->val); l1 = l1->next; } while (l2) { s2.push(l2->val); l2 = l2->next; }

2.2 逐位相加与进位处理

从两个栈顶同时取出数字进行相加,需要注意:

  1. 处理栈长度不等的情况(短的栈视为0)
  2. 当前位和为两数之和加上进位
  3. 新进位为和除以10的整数部分
  4. 当前位值为和对10取余
int carry = 0; while (!s1.empty() || !s2.empty() || carry) { int num1 = s1.empty() ? 0 : s1.top(); int num2 = s2.empty() ? 0 : s2.top(); if (!s1.empty()) s1.pop(); if (!s2.empty()) s2.pop(); int sum = num1 + num2 + carry; carry = sum / 10; int curVal = sum % 10; // 头插法构建结果链表 ListNode* newNode = new ListNode(curVal); newNode->next = head; head = newNode; }

2.3 头插法构建结果链表

由于计算顺序是从低位到高位,而结果链表要求高位在前,因此需要使用头插法:

  1. 每次创建一个新节点存储当前位值
  2. 将新节点的next指向当前结果链表的头节点
  3. 更新头节点为新节点

这种方法保证了最终链表的顺序与计算顺序相反,符合题目要求。

3. 完整代码实现与解析

3.1 C++实现

class Solution { public: ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) { stack<int> s1, s2; // 将链表节点值压入栈 while (l1) { s1.push(l1->val); l1 = l1->next; } while (l2) { s2.push(l2->val); l2 = l2->next; } int carry = 0; ListNode* head = nullptr; // 逐位相加 while (!s1.empty() || !s2.empty() || carry) { int num1 = s1.empty() ? 0 : s1.top(); int num2 = s2.empty() ? 0 : s2.top(); if (!s1.empty()) s1.pop(); if (!s2.empty()) s2.pop(); int sum = num1 + num2 + carry; carry = sum / 10; int curVal = sum % 10; // 头插法构建链表 ListNode* newNode = new ListNode(curVal); newNode->next = head; head = newNode; } return head; } };

3.2 JavaScript实现

var addTwoNumbers = function(l1, l2) { const s1 = [], s2 = []; // 将链表节点值压入栈 while (l1) { s1.push(l1.val); l1 = l1.next; } while (l2) { s2.push(l2.val); l2 = l2.next; } let carry = 0; let head = null; // 逐位相加 while (s1.length || s2.length || carry) { const num1 = s1.length ? s1.pop() : 0; const num2 = s2.length ? s2.pop() : 0; const sum = num1 + num2 + carry; carry = Math.floor(sum / 10); const curVal = sum % 10; // 头插法构建链表 const newNode = new ListNode(curVal); newNode.next = head; head = newNode; } return head; };

4. 复杂度分析与优化思考

4.1 时间复杂度分析

  • 遍历链表压栈:O(m + n)
  • 出栈相加:O(max(m, n))
  • 总时间复杂度:O(m + n)

4.2 空间复杂度分析

  • 两个栈的存储空间:O(m + n)
  • 结果链表空间:O(max(m, n) + 1)
  • 总空间复杂度:O(m + n)

4.3 可能的优化方向

  1. 空间优化:可以不使用额外栈,而是先反转链表,再进行相加,最后再反转结果链表。这样空间复杂度可降为O(1),但需要多次遍历链表。

  2. 并行处理:对于超长链表,可以考虑并行处理压栈过程。

5. 常见问题与调试技巧

5.1 典型错误案例

  1. 忘记处理最后进位:

    • 错误表现:999 + 1 = 000
    • 解决方法:确保循环条件包含carry != 0
  2. 头插法顺序错误:

    • 错误表现:结果链表顺序与预期相反
    • 解决方法:检查新节点next指向和head更新顺序
  3. 栈操作错误:

    • 错误表现:数值计算不正确
    • 解决方法:确保先取top再pop,且处理栈空情况

5.2 调试建议

  1. 打印中间状态:

    • 压栈后打印栈内容
    • 每次相加后打印当前位值和进位
  2. 小规模测试:

    • 从简单案例开始(如1位数相加)
    • 逐步增加复杂度
  3. 边界测试:

    • 一个链表为空
    • 结果比原链表长(进位导致)

6. 扩展思考与实际应用

6.1 类似问题变种

  1. 链表表示的数字相减:需要考虑借位而非进位
  2. 链表表示的数字相乘:需要嵌套循环处理
  3. 浮点数链表相加:需要处理小数点对齐

6.2 实际应用场景

  1. 大整数运算:超出基本数据类型范围的整数运算
  2. 高精度计算:科学计算、金融领域
  3. 分布式系统:数据分片后的聚合计算

6.3 算法选择思考

当面对类似问题时,选择数据结构的思考过程:

  1. 确定操作特性(需要反向访问 → 栈)
  2. 评估时间/空间复杂度
  3. 考虑代码实现复杂度
  4. 测试边界条件

7. 个人实践心得

在实际解决这个问题时,我总结了以下几点经验:

  1. 画图辅助:在纸上画出栈操作和链表构建过程,能极大帮助理解
  2. 分步验证:先实现压栈功能,验证正确后再实现相加逻辑
  3. 测试驱动:先写测试用例,再实现功能,确保覆盖所有边界条件
  4. 代码复用:识别出与常规链表相加问题的共性部分(如进位处理)

对于JavaScript实现,特别注意:

  • 数组的push/pop效率很高,可以放心使用
  • 使用Math.floor处理进位,避免小数问题
  • 链表判空使用if(node)这种简洁写法

最后,这类问题的解决关键在于:

  1. 识别问题的核心难点(反向访问)
  2. 选择合适的数据结构(栈)
  3. 正确处理边界条件(进位、空链表)
  4. 构建结果时的顺序处理(头插法)
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/11/4 17:44:27

MSYS2:Windows下的高效Unix开发环境搭建指南

1. MSYS2&#xff1a;Windows下的类Unix开发环境解决方案 作为一名在Windows平台进行C/C开发多年的程序员&#xff0c;我深知在Windows上搭建一个高效、稳定的开发环境有多么痛苦。直到遇到MSYS2&#xff0c;这个基于Arch Linux包管理系统的工具集合彻底改变了我的工作流。MSYS…

作者头像 李华
网站建设 2026/11/4 17:42:23

红莲花AI浏览器实测:混合架构如何让浏览器真正“懂你”

上个月我拿到了红莲花AI浏览器的内测邀请&#xff0c;第一反应是"又一个套壳浏览器"。但用了一周之后&#xff0c;我发现自己这个判断下得太早了。它在"让浏览器比你更懂你"这件事上&#xff0c;确实不是口号&#xff0c;20多项智能功能里有一半以上是我每…

作者头像 李华
网站建设 2026/11/4 17:36:36

Spring Boot多数据源与分库分表实战:路由、事务、分片键与迁移验收

简介&#xff1a;围绕多数据源与数据库分库分表展开的实战工程&#xff0c;面向已掌握Spring Boot基础、希望理解同库水平分表与多数据源切换的开发者。项目以Spring Boot搭建&#xff0c;多数据源部分采用MyBatis-Plus的dynamic-datasource&#xff0c;分库分表引入Sharding-J…

作者头像 李华
网站建设 2026/11/4 17:32:08

Claude Code 报错排查:ECONNRESET 与 API ERROR

某天下午&#xff0c;我在终端里敲下claude&#xff0c;回车&#xff0c;光标闪了两下&#xff0c;正准备把一段两百行的重构需求贴进去&#xff0c;屏幕上先甩出来一行红字&#xff1a;Unable to connect to API (ECONNRESET)。再试一次&#xff0c;变成API ERROR&#xff0c;…

作者头像 李华
网站建设 2026/11/4 17:32:05

中国模型反向收费美国巨头:技术授权与按量分成实战

1. 从“免费换量”到“按量收费”的转折点 “这一次&#xff0c;中国模型开始收美国巨头的钱”——这句话第一次看到的时候&#xff0c;我正蹲在工位上啃三明治&#xff0c;差点没噎着。做了七八年AI应用层开发&#xff0c;我太清楚过去几年国内大模型厂商在海外的处境了&#…

作者头像 李华
网站建设 2026/11/4 17:28:43

低代码开发平台全解析:架构引擎、应用场景与选型边界

简介&#xff1a;这是一份由头豹研究院出品的行业研究报告PDF&#xff0c;聚焦2022年中国低代码/无代码平台&#xff0c;面向企业数字化负责人、产品经理、低代码厂商从业者及关注该赛道的投资研究者&#xff0c;帮助他们在平台选型、部署决策与商业判断上建立系统认知。报告围…

作者头像 李华