当前位置: 首页 > news >正文

LeetCode 2 - 两数相加

LeetCode 2 - 两数相加 是一道经典链表操作问题,经常作为面试中基础题的变体被考察。掌握多种解法及其变体,并熟悉其核心思路和模板代码,可以快速备战相关链表或大数计算问题。


题目描述

给定两个非空链表,它们代表两个非负整数,每个节点保存一个数字,数字按 逆序存储(个位在链表头部),求两个数的和并返回一个新的链表,表示其逆序和。

  • 输入:两个链表,l1 表示整数 a,l2 表示整数 b。
  • 输出:表示结果 a + b 的链表。

示例:

输入: l1 = [2,4,3], l2 = [5,6,4]
输出: [7,0,8]
解释: 342 + 465 = 807

解法及其分析

解法 1:模拟加法(逐位相加)

思路
  • 链表表示逆序存储的数字,相当于按从低位到高位逐位对齐相加,因此直接模拟两数加法过程:
    1. 遍历两个链表,从头到尾逐位相加,考虑进位。
    2. carry 表示当前进位值(初始为 0)。
    3. 若链表长度不相等,可补 0,即看成空位处理。
    4. 将每次相加后的结果(sum % 10)保存到新链表中。
  • 完成遍历后,如果 carry 不为 0,则需要在结果链表末尾补 1。
模板代码
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode dummyHead = new ListNode(0); // 哑节点
        ListNode p1 = l1, p2 = l2, current = dummyHead;
        int carry = 0;

        // 遍历两个链表
        while (p1 != null || p2 != null) {
            int x = (p1 != null) ? p1.val : 0; // 当节点为空时看作 0
            int y = (p2 != null) ? p2.val : 0;

            int sum = x + y + carry; // 当前位和
            carry = sum / 10;       // 计算进位
            current.next = new ListNode(sum % 10); // 新节点保存当前位值
            current = current.next;

            // 前进指针
            if (p1 != null) p1 = p1.next;
            if (p2 != null) p2 = p2.next;
        }

        // 如果最后进位不为 0,添加额外节点
        if (carry > 0) {
            current.next = new ListNode(carry);
        }

        return dummyHead.next; // 返回去掉哑节点后的结果链表
    }
}
复杂度分析
  • 时间复杂度: O(max(m, n)),其中 mn 是两个链表的长度,需遍历长链表的每个节点。
  • 空间复杂度: O(1),除了结果链表外,没有使用额外空间。

解法 2:递归解法

思路
  • 使用递归模拟逐位相加的过程:
    • 每次处理一对链表节点,计算当前位的和以及进位。
    • 递归调用处理剩余链表部分,进位值通过函数参数传递。
  • 递归本质就是逆序链表的“从尾到头”的回溯计算。
模板代码
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        return helper(l1, l2, 0);
    }

    private ListNode helper(ListNode l1, ListNode l2, int carry) {
        if (l1 == null && l2 == null && carry == 0) {
            return null; // 递归结束条件
        }

        int sum = carry;
        if (l1 != null) sum += l1.val;
        if (l2 != null) sum += l2.val;

        ListNode node = new ListNode(sum % 10); // 保存当前位
        node.next = helper(
            (l1 != null) ? l1.next : null, 
            (l2 != null) ? l2.next : null, 
            sum / 10 // 进位传递到下一层递归
        );

        return node;
    }
}
复杂度分析
  • 时间复杂度: O(max(m, n)),需要递归处理每一位。
  • 空间复杂度: O(max(m, n)),递归调用栈的深度最大为链表长度。

解法 3:反转链表法(适用于顺序存储时)

核心问题

如果链表存储的是整数的正常顺序(高位在前),如何求两数和?

思路
  1. 若链表采用正序存储,则需要先将链表反转(采用常用 反转链表模板)。
  2. 使用解法 1 的逐位相加方法,计算链表和。
  3. 最后将结果链表反转,恢复顺序。
模板代码
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        l1 = reverseList(l1);
        l2 = reverseList(l2);
        ListNode result = addTwoNumbersReversed(l1, l2);
        return reverseList(result);
    }

    // 反转链表
    private ListNode reverseList(ListNode head) {
        ListNode prev = null;
        while (head != null) {
            ListNode next = head.next;
            head.next = prev;
            prev = head;
            head = next;
        }
        return prev;
    }

    // 借助解法1,计算反转链表的和
    private ListNode addTwoNumbersReversed(ListNode l1, ListNode l2) {
        ListNode dummyHead = new ListNode(0);
        ListNode current = dummyHead;
        int carry = 0;

        while (l1 != null || l2 != null || carry != 0) {
            int x = (l1 != null) ? l1.val : 0;
            int y = (l2 != null) ? l2.val : 0;
            int sum = x + y + carry;

            current.next = new ListNode(sum % 10);
            carry = sum / 10;
            current = current.next;

            if (l1 != null) l1 = l1.next;
            if (l2 != null) l2 = l2.next;
        }
        return dummyHead.next;
    }
}
复杂度分析
  • 时间复杂度: O(m + n),反转链表和加法操作总共需要线性时间。
  • 空间复杂度: O(1),原地操作链表。

变体问题

1. 链表与整数数组转换
  • 将链表转换为整数数组,再处理运算,最后将结果转换回链表。
  • 对于大整数字符串输入,也可以使用类似思路。
2. 两个数字是否相等
  • 判断两个链表存储的整数是否相等。
3. 合并 k 个链表的和
  • 扩展为 k 个链表相加,这可以使用优先队列或逐链表合并。
4. 大数相加(整数以字符串形式存储)
  • 给定两个数字以字符串形式存储,模拟大数加法逻辑。
  • 和这道题的链表加法思路非常类似。
5. 减法模拟
  • 如果要求两个链表表示的数相减,稍作修改:
    • 按位模拟减法。
    • 借位处理时,考虑前一位减 1。

快速 AC 总结

  1. 熟记关键模块
    • 使用链表构造哑节点(dummyHead)。
    • 逐位相加时的循环结构与边界条件。
    • 进位处理逻辑(如 carry)。
  2. 应对变体问题
    • 若逆序时直接逐位相加,正序时先反转链表。
    • 若输入为整数数组或大数字符串,则手动模拟链表结构。
  3. 调试基础功能(链表遍历、构建和反转)
    • 熟悉链表的单步操作和空链表边界处理,无论变体或难度增加都可以高效应对。

通过练习核心模板和理解变体模式,可以轻松解决这类链表和数字计算相关的题目。

相关文章:

  • 关于博客系统的测试报告
  • 机器人学习模拟框架 robosuite (3) 机器人控制代码示例
  • 【redis】redis持久化
  • Mysql的基础命令有哪些?
  • 学习Java:全面解析构造方法重载详解
  • 如何为Java面试准备项目经验
  • JDBC、Mybatis基础知识
  • C++编程指南20 - 使用 joining_thread以确保线程不会在变量生命周期之外运行
  • 20250302让chrome打开刚关闭的网页
  • 计算机毕业设计Hadoop+Spark+DeepSeek-R1大模型音乐推荐系统 音乐数据分析 音乐可视化 音乐爬虫 知识图谱 大数据毕业设计
  • 基因枷锁下的太空梦 —— 千钧一发电影观后感
  • 《HarmonyOS Next × ArkTS框架:从AI模型压缩到智能家居控制的端侧开发指南》
  • 算法--贪心
  • 基于SpringBoot + Vue的商城购物系统实战
  • 非平稳时间序列分析(二)——ARIMA(p, d, q)模型
  • unity大坐标抖动处理测试
  • UNIAPP前端配合thinkphp5后端通过高德API获取当前城市天气预报
  • TCP和UDP比较
  • 使用mybatis plus的@Select自定义sql时,如何实现通用的分页查询?
  • MacDroid for Mac v2.3 安卓手机文件传输助手 支持M、Intel芯片 4.7K
  • 西宁那有做网站的/怎么做信息流广告代理商
  • 有人用dw做网站吗/西安seo排名
  • 东莞樟木头做网站哪家好/平台seo
  • 软件项目管理软件/seo技术培训教程视频
  • 怎么制作一个网站及小程序/完美日记网络营销策划书
  • b2b电子商务网站分类/网站不收录怎么解决