C++ 实现大数加法
一、问题描述
- 大数的加法即两个非常大的数字进行相加,数字的大小超过了long long的表示范围(溢出)。由于数字可能非常大,不能直接转换为内置整型计算,必须逐位模拟竖式相加。
- 例如,要求实现两个非负整数的加法(负数的话直接算完前面加个负号即可),输入的数值为a = 42312313432351231276453587647867123561273189378912673678和b = 239123128973127663451234523478634675781267814562345612837324,请输出它们的和。
二、算法思想(竖式相加)
- 从两数的最低位(字符串最后一个字符)开始逐位相加。
- 每一位的计算包括两个对应位的值以及来自低位的进位(carry)。
- 若相加结果 ≥ 10,则该位结果为
sum - 10,并向高位产生进位 1。 - 当两个字符串长度不同,较长数剩余的位要继续加上可能存在的进位。
- 最终若最高位仍有进位,则在结果最前面补上
1。 - 将每位结果拼接回字符串并返回(注意去掉可能的前导零)。
三、示例代码解析(核心思想与实现)

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
typedef long long ll;/*将按位存放的数组转为字符串,并忽略前导零参数 arr: 每个元素是 0~9 的位值(高位在前,低位在后),可能有前导 0返回值: 去除前导零后的数字字符串;如果全为 0 则返回空字符串(使用者可判定)
*/
std::string num_arr_to_string(std::vector<int> arr){std::string a("");bool begin = false; // 标记是否遇到第一个非零位for (int c: arr){if (c != 0){begin = true;}if (begin){a += std::to_string(c);}}return a;
}// 调试用:打印数组中的每个元素(空格分隔)
void show_arr(std::vector<int> arr){for (int c: arr){std::cout << c << " ";}std::cout << std::endl;
}// 检查一个字符串是否全为 0
bool is_zero_str(std::string a){for (char c: a){if (c != '0')return false;}return true;
}/*大数相加(把结果保存在字符串中)输入:a, b —— 表示两个非负整数的字符串(例如 "123", "9")输出:a + b 的结果(字符串形式)说明:- 作者用一个整型数组 arr 保存计算过程的每一位,最后再转成字符串返回- 注意:代码里把 arr 的长度设为 lens = max_len * max_len,这里长度远大于实际需要。这样做虽然“安全”但浪费空间。后面博客会指出更合理的做法:长度为 max_len + 1 即可。
*/
std::string big_num_add(std::string a, std::string b){if (is_zero_str(a) && is_zero_str(b)){return "0";}int max_len = std::max(a.length(), b.length());ll lens = max_len * max_len; // 原代码使用 max_len*max_len(过度分配)std::vector<int> arr(lens + 1, 0); // 存放每一位的结果(高位在前,低位在后),初始化为 0bool jin = false; // 进位标记(carry),true 表示上一位有进位 1int i = a.length() - 1; // 指向字符串 a 的最低位(从后向前遍历)int j = b.length() - 1; // 指向字符串 b 的最低位int idx = lens; // arr 的填充索引(从后向前填充)// 同时处理 a 和 b 都还有位的情况while (i >= 0 and j >= 0){int xi = a[i] - '0'; // 当前 a 的位(0-9)int xj = b[j] - '0'; // 当前 b 的位(0-9)int num = xi + xj + jin; // 当前位相加(含进位)if (num >= 10){jin = true;num -= 10; // 只保留当前位}else{jin = false;}arr[idx] = num; // 存入当前位i -= 1;j -= 1;idx -= 1;}// 如果 a 还有剩余位,继续处理(把进位加到剩余位上)if (i >= 0){while(i >= 0){int xi = a[i] - '0';int num = xi + jin;if (num >= 10){jin = true;num -= 10;arr[idx] = num;}else{jin = false;arr[idx] = num;}i -= 1;idx -= 1;}// 若遍历完仍然有进位,把 1 放到当前 idx 处(高位)if (jin) { arr[idx] = 1; }}// 如果 b 还有剩余位,继续处理(逻辑与上面对称)if (j >= 0){while(j >= 0){int xj = b[j] - '0';int num = xj + jin;if (num >= 10){jin = true;num -= 10;arr[idx] = num;}else{jin = false;arr[idx] = num;}j -= 1;idx -= 1;}if (jin) { arr[idx] = 1; }}if (jin) {arr[idx] = 1;} // 没有分支时的进位// show_arr(arr); // 可启用来查看数组计算状态(调试用)return num_arr_to_string(arr); // 把结果数组转换为字符串返回(去除前导零)
}int main(){std::string a, b;std::cin >> a >> b; // 从标准输入读取两个字符串(假设都是非负整数且无额外空格)std::string res = big_num_add(a, b);std::cout << res; return 0;
}
- 存储结构:代码使用
std::vector<int> arr来保存每一位的结果(高位在前,低位在后),最后通过num_arr_to_string去掉前导零并转换为字符串返回。 - 进位处理:用布尔变量
jin表示当前是否有进位(true表示有 1 的进位)。在每一步相加后更新jin。 - 数组大小:原代码中将
arr的长度设为max_len * max_len,这是一种“超保守”的做法——保证数组足够大,但会严重浪费空间。更合理的做法是把数组长度设为max_len + 1,因为两个长度为max_len的数相加最多产生一个额外的最高位进位。 - 返回值:
num_arr_to_string会去掉前导零;若结果为零,当前实现会返回空字符串(这点可以改进为返回"0",更符合直觉)。
四、代码逐行要点说明(关键处总结)
int i = a.length() - 1, j = b.length() - 1;:从两数的最低位开始处理。int num = xi + xj + jin;:加上三个部分:a 的位、b 的位、进位。- 若
num >= 10,设置jin=true并num-=10,否则jin=false。 - 遍历结束后,若仍有
jin,需在更高位写入1。 - 最后调用
num_arr_to_string去掉高位多余的 0 并得到字符串结果。
五、时间与空间复杂度
- 时间复杂度:
O(n),其中n = max(len(a), len(b)),每个位最多被访问常数次。 - 空间复杂度:原代码
O(L^2)(因为用了max_len * max_len),但最合理的实现只需O(n)(长度n + 1的数组或直接构造字符串存放结果)。
八、总结
大数运算是很多竞赛与工程场景常见的基础问题,掌握逐位相加、进位处理,以及如何在字符串/数组与结果字符串之间高效转换,是写出可靠代码的关键。
