每日算法 -【Swift 算法】链表版两数相加 —— 简单易懂讲透它!

【Swift】链表版两数相加 —— 简单易懂讲透它!

1. 题目简介

给你两个 非空链表,每个节点存储一位数字,逆序排列,代表两个非负整数。

你要返回它们的,结果也用链表表示,也是逆序排列

✅ 题目保证:除了数字 0 以外,不会有前导零。

例子:

输入:
l1 = 2 -> 4 -> 3  (表示 342)
l2 = 5 -> 6 -> 4  (表示 465)

输出:
7 -> 0 -> 8  (表示 807)

2. 思路讲解

其实就是模拟我们平时写的竖式加法:

  • 个位 + 个位,算出结果,判断是否要进位。
  • 然后十位 + 十位 + 进位,继续。
  • 一直到没有数字了,最后别忘了还可能有进位。

链表是逆序的,其实是帮我们把“从低位到高位加”这件事搞定了,非常贴心。

3. 代码实现(Swift 版)

public class ListNode {
    public var val: Int
    public var next: ListNode?
    public init() { self.val = 0; self.next = nil; }
    public init(_ val: Int) { self.val = val; self.next = nil; }
    public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; }
}

class Solution {
    func addTwoNumbers(_ l1: ListNode?, _ l2: ListNode?) -> ListNode? {
        let dummyHead = ListNode(0)
        var p = l1, q = l2, current = dummyHead
        var carry = 0

        while p != nil || q != nil {
            let x = p?.val ?? 0
            let y = q?.val ?? 0
            let sum = carry + x + y
            carry = sum / 10
            current.next = ListNode(sum % 10)
            current = current.next!

            if p != nil { p = p!.next }
            if q != nil { q = q!.next }
        }

        if carry > 0 {
            current.next = ListNode(carry)
        }

        return dummyHead.next
    }
}

// 辅助方法:创建链表
func createLinkedList(_ numbers: [Int]) -> ListNode? {
    let dummy = ListNode(0)
    var current = dummy
    for num in numbers {
        current.next = ListNode(num)
        current = current.next!
    }
    return dummy.next
}

// 辅助方法:打印链表
func printLinkedList(_ node: ListNode?) {
    var current = node
    var values: [String] = []
    while current != nil {
        values.append("\(current!.val)")
        current = current!.next
    }
    print(values.joined(separator: " -> "))
}

// 调用示例
let l1 = createLinkedList([2, 4, 3])
let l2 = createLinkedList([5, 6, 4])

let solution = Solution()
let result = solution.addTwoNumbers(l1, l2)

printLinkedList(result)  // 输出:7 -> 0 -> 8

4. 分步演示

步骤 操作 变量状态 结果链表
1 初始化 carry=0 dummy → None
2 第一次循环:2+5+0=7 val1=2, val2=5, carry=0 dummy → 7
3 第二次循环:4+6+0=10 val1=4, val2=6, carry=1 dummy → 7 → 0
4 第三次循环:3+4+1=8 val1=3, val2=4, carry=0 dummy → 7 → 0 → 8
5 循环结束,返回结果 carry=0 最终结果:[7,0,8]

5. 复杂度分析

  • 时间复杂度:O(max(m, n))

    • 其中 m 和 n 是两个链表的长度。
    • 每一位只遍历一次,所以取决于较长链表的长度。
  • 空间复杂度:O(max(m, n))

    • 需要额外的链表空间来存储结果,最多比最长链表多一位(进位)。

6. 其他解法(暴力解法 vs 优雅链表法)

6.1 暴力解法(不推荐)

  • 把两个链表转换成整数(先反转回来),直接相加,再转成链表。
  • 但是:
    • Swift 中整数溢出风险大(超过 Int64 就 GG 了)
    • 链表题考察的本质是链表操作,而不是单纯的数学。
伪代码:
1. 链表转整数
2. 相加
3. 整数转链表(逆序)

✅ 适用于“小数字”的投机取巧,但面试、比赛中不推荐。

6.2 优雅链表解法(推荐)

  • 当前代码就是“从低位到高位逐位相加”,模拟加法竖式,天然避免了溢出问题。
  • 代码鲁棒性更强,也体现了链表的应用价值。

7. 核心知识点总结

知识点 说明
链表遍历 两个链表一起遍历
处理进位 每次相加后都要考虑进位
虚拟头节点 让链表插入操作更方便
链表节点为 nil 时当 0 处理 避免越界
时间 & 空间复杂度 O(max(m, n))

8. 总结

这道题,其实就是「模拟手算加法」+「链表遍历」的结合。逆序排列正好符合我们从低位加起的习惯,代码写出来一看就明白!

你可能感兴趣的:(算法,算法,swift,链表)