【小白笔记】大数加法

“大数加法”是面试和算法练习中非常经典的一道题(如 LeetCode 415)。它的核心矛盾在于:计算机的标准整数类型(如 64 位整型)有最大值限制,当数字超过几十位甚至上百位时,直接相加会溢出。

处理这个问题的通用方案是:模拟人类列竖式计算的过程


1. 核心逻辑:从低位到高位

就像我们在纸上做加法一样:

  1. 对齐:从两个字符串的末尾(个位)开始加。
  2. 进位 (carry):如果相加结果 ≥10\ge 1010,记下进位,留到下一位使用。
  3. 补齐:如果一个数字比另一个短,短的部分补 0
  4. 最后进位:如果所有位都加完了,进位还剩 1,别忘了在最高位补个 1

2. 代码实现 (Python)

class Solution:
    def addStrings(self, num1: str, num2: str) -> str:
        # 指针分别指向两个字符串的末端
        i, j = len(num1) - 1, len(num2) - 1
        carry = 0  # 进位
        res = []   # 存储每一位的结果
        
        # 只要还有位没加完,或者还有进位没处理
        while i >= 0 or j >= 0 or carry:
            # 取出当前位的值,如果指针已经越界则补 0
            x = int(num1[i]) if i >= 0 else 0
            y = int(num2[j]) if j >= 0 else 0
            
            # 计算当前位的总和
            total = x + y + carry
            
            # 更新进位和当前位
            carry = total // 10
            res.append(str(total % 10))
            
            # 指针左移
            i -= 1
            j -= 1
            
        # 因为是从个位开始存的,最后需要翻转回来并拼接成字符串
        return "".join(res[::-1])

3. 关键点拆解

A. 为什么用 res[::-1]

我们在循环里是先算出“个位”,再算“十位”,存入列表的结果是 ["个", "十", "百"]。这和我们阅读数字的顺序是相反的,所以最后需要 [::-1](反转)一下。

B. while i >= 0 or j >= 0 or carry:

这个条件非常精妙:

  • i >= 0 or j >= 0:保证了即使两个数字长度不等,也能把长的那个加完。
  • or carry:处理最特殊的情况,比如 99 + 1。当 ij 都走完了,carry 还是 1,循环会多跑一次,把最高位的 1 加上去。
C. total // 10total % 10
  • total // 10:取商。如果是 13,商就是 1,作为进位。
  • total % 10:取余。如果是 13,余数就是 3,作为当前位的结果。

4. 复杂度分析

  • 时间复杂度O(max(N,M))O(max(N, M))O(max(N,M))N,MN, MN,M 分别为两个数字的长度。我们需要遍历较长的那个数字。
  • 空间复杂度O(max(N,M))O(max(N, M))O(max(N,M))。我们需要一个列表来存放结果。

        
        # 只要还有位没加完,或者还有进位没处理
        while i >= 0 or j >= 0 or carry:
            # 取出当前位的值,如果指针已经越界则补 0
            x = int(num1[i]) if i >= 0 else 0
            y = int(num2[j]) if j >= 0 else 0
            

在 Python 中,x = A if condition else B 叫做条件表达式(也叫三元运算符)。如果你觉得它看起来太挤,完全可以用标准的 if-else 结构来替换。

写开后的代码(更易读版)

我们将这两个赋值动作拆解到循环内部:

while i >= 0 or j >= 0 or carry:
    # --- 处理 num1 的当前位 ---
    if i >= 0:
        x = int(num1[i])
    else:
        x = 0  # 如果 num1 已经遍历完了,就当它是 0
        
    # --- 处理 num2 的当前位 ---
    if j >= 0:
        y = int(num2[j])
    else:
        y = 0  # 如果 num2 已经遍历完了,就当它是 0
        
    # --- 后面的逻辑不变 ---
    total = x + y + carry
    carry = total // 10
    res.append(str(total % 10))
    
    i -= 1
    j -= 1

为什么我们要这么写?(补零逻辑)

这种写法是为了解决**“两个数字长度不一样”**的问题。

比如计算 123 + 45

  1. 第一轮i 指向 3j 指向 5x=3, y=5
  2. 第二轮i 指向 2j 指向 4x=2, y=4
  3. 第三轮i 指向 1,但 j 已经变成 -1 了(越界)。
    • 此时,如果没有 else: y = 0 这段代码,程序就会报错。
    • 有了这段代码,y 就会自动补 0。这就相当于你在纸上算:
        1 2 3
      + 0 4 5  <-- 这里的0就是靠 else 分支补上的
      ---------
      

这种写法的好处

  1. 调试方便:如果你想看 x 或者 y 到底取到了什么值,你可以在 if 分支里打断点或加 print
  2. 逻辑安全:它显式地处理了索引越界的情况,不会出现 IndexError

  • int(num1[i]):代码中将字符转换为整数的关键步骤。我们可以把它拆成三部分来理解:

1. 拆解分析

  • num1[i]
    • num1 是一个字符串(比如 "123")。
    • i 是当前的索引(下标)。
    • 所以 num1[i] 是从字符串中取出i 个位置的字符。比如 num1[0] 取出的是 "1"(注意,此时它还是带引号的字符串,不能直接做加法)。
  • int(...)
    • 这是一个内置函数,负责强制类型转换
    • 它把括号里的东西转成整数。比如把字符串 "1" 转成数字 1
  • x = ...
    • 把转换后的数字赋值给变量 x,这样 x 就可以参与数学运算了。

2. 为什么要加 int()

在 Python 中,字符串数字是两种完全不同的“物种”。

如果你直接把两个字符串相加,Python 会把它们拼接起来:

"1" + "2" = "12" (这显然不是我们要的加法结果)

如果你把字符串和数字相加,Python 会报错

"1" + 2 = TypeError (因为系统不知道你是想拼接还是想求和)

所以,我们需要用 int() 把字符变成数字,才能进行真正的数学运算:

int("1") + int("2") = 3


3. 放到大数加法背景下

假设我们要算 "98" + "7"

  1. i 指向 num1 的末尾(字符 "8")时:
    • x = int(num1[i]) 得到 x = 8
  2. j 指向 num2 的末尾(字符 "7")时:
    • y = int(num2[j]) 得到 y = 7
  3. 这时我们就可以算:8 + 7 = 15,然后处理进位。

4. 这种写法的一个“坑”

在实际运行这行代码之前,必须确保 num1[i] 确实是一个代表数字的字符(比如 '0'-'9')。

  • 如果是 int("a"),代码会报错:ValueError
  • 在大数加法里,因为我们已经用 if i >= 0 保证了索引有效,且题目保证输入是数字字符串,所以这么写是安全的。

你会发现: 在很多涉及数字字符串处理的题目中(比如求和、乘法、有效数字判断),int(s[i]) 都是出镜率最高的一行代码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值