011 【入门】链表入门题目-两个链表相加

链表入门:两个链表相加算法解析

🔗 链表相加算法 | [算法]-[中级]-[链表]

▶ JDK8+ | ⏱️ O(max(M,N))

问题描述

给定两个非空链表,分别表示两个非负整数,满足:

  1. 数字逆序存储(头节点是个位)
  2. 每个节点只存储一位数字

请将两数相加,返回相同形式的链表

示例

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

核心解法

迭代解法(推荐)

/**
 * 两数链表相加方法
 * @param l1 第一个链表,表示第一个数字(逆序)
 * @param l2 第二个链表,表示第二个数字(逆序)
 * @return 返回相加结果的链表(逆序)
 */
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
    ListNode dummy = new ListNode(0); // 虚拟头节点(简化操作)
    ListNode cur = dummy;   
    int carry = 0;          // 进位值
    
    // 三条件确保完全处理:链表未结束或仍有进位
    while (l1 != null || l2 != null || carry != 0) {
        int sum = carry;    // 从进位开始累加
        
        // 安全获取链表值(空则取0)
        if (l1 != null) {
            sum += l1.val;
            l1 = l1.next;
        }
        if (l2 != null) {
            sum += l2.val;
            l2 = l2.next;
        }
        
        carry = sum / 10;             // 计算新进位
        cur.next = new ListNode(sum % 10);  // 创建结果节点
        cur = cur.next;               // 移动指针
    }
    return dummy.next; // 返回真实头节点
}

关键技巧

  1. 虚拟头节点(dummy)

    • 避免空链表特殊处理
    • 统一节点添加逻辑
    • [最佳实践] 链表操作常用技巧
  2. 循环三条件
    while (l1 != null || l2 != null || carry != 0)

    • 确保链表不等长时仍能计算
    • 单独处理最后进位(如:9+1=10)
    • [注意] 三条件缺一不可,确保完整性
  3. 安全取值

    // [防御式编程] 空值安全处理
    if (l1 != null) sum += l1.val;
    if (l2 != null) sum += l2.val;
    

复杂度分析

维度复杂度说明
时间O(max(M,N))遍历较长链表
空间O(max(M,N))新建结果链表

边界处理

graph LR
A[开始] --> B{双链表存在?}
B -->|是| C[计算节点和+进位]
B -->|否| D[检查进位]
C --> E[创建新节点]
E --> F[移动所有指针]
F --> B
D --> |进位>0| G[添加新节点]
D --> |进位=0| H[结束]
G --> H

特殊测试用例

  1. 进位传递
    99 + 1 = 100 → 输入:[9,9] + [1],输出:[0,0,1]

  2. 不等长链表
    123 + 45678 = 45801 → 输入:[3,2,1] + [8,7,6,5,4]

  3. 零值处理
    0 + 0 = 0 → 输入:[0] + [0],输出:[0]

优化与扩展

递归实现(简洁但不推荐)

/**
 * 递归方式实现两数链表相加
 * @param l1 第一个链表
 * @param l2 第二个链表
 * @param carry 进位值
 * @return 返回相加结果的链表
 */
public ListNode addTwoNumbersRecursive(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 result = new ListNode(sum % 10);
    result.next = addTwoNumbersRecursive(
        l1 != null ? l1.next : null,
        l2 != null ? l2.next : null,
        sum / 10
    );
    
    return result;
}

大数加法关联

// [关联算法] 字符串大数加法
public String addStrings(String num1, String num2) {
    StringBuilder res = new StringBuilder();
    int i = num1.length() - 1, j = num2.length() - 1;
    int carry = 0;
    
    while (i >= 0 || j >= 0 || carry != 0) {
        int x = i >= 0 ? num1.charAt(i) - '0' : 0;
        int y = j >= 0 ? num2.charAt(j) - '0' : 0;
        int sum = x + y + carry;
        
        res.append(sum % 10);
        carry = sum / 10;
        
        i--; j--;
    }
    
    return res.reverse().toString();
}

学习价值

  1. 链表基础操作:指针移动+节点创建
  2. 进位处理模式:大数运算通用方法
  3. 边界思维训练:空指针/不等长/进位传递

技术决策树

较短
极长
链表相加问题
链表长度?
迭代解法
警惕递归栈溢出
虚拟头节点技巧
考虑迭代实现
处理不等长情况
处理进位传递

相关题目

🔗 相关题目:

🚀 效率工具

💡 面试技巧:先解释虚拟头节点作用,再手写代码,最后用测试用例验证边界

扩展问题:如何处理两个链表正序存储数字的情况?(提示:考虑栈或链表反转)

视频链接

左老师的视频链接

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值