C++ 实现大数加法

一、问题描述

  • 大数的加法即两个非常大的数字进行相加,数字的大小超过了long long的表示范围(溢出)。由于数字可能非常大,不能直接转换为内置整型计算,必须逐位模拟竖式相加。
  • 例如,要求实现两个非负整数的加法(负数的话直接算完前面加个负号即可),输入的数值为a = 42312313432351231276453587647867123561273189378912673678和b = 239123128973127663451234523478634675781267814562345612837324,请输出它们的和。

二、算法思想(竖式相加)

  1. 从两数的最低位(字符串最后一个字符)开始逐位相加。
  2. 每一位的计算包括两个对应位的值以及来自低位的进位(carry)。
  3. 若相加结果 ≥ 10,则该位结果为 sum - 10,并向高位产生进位 1。
  4. 当两个字符串长度不同,较长数剩余的位要继续加上可能存在的进位。
  5. 最终若最高位仍有进位,则在结果最前面补上 1
  6. 将每位结果拼接回字符串并返回(注意去掉可能的前导零)。

三、示例代码解析(核心思想与实现)

c 复制代码
#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); // 存放每一位的结果(高位在前,低位在后),初始化为 0

    bool jin = false; // 进位标记(carry),true 表示上一位有进位 1
    int 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=truenum-=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 的数组或直接构造字符串存放结果)。

八、总结

大数运算是很多竞赛与工程场景常见的基础问题,掌握逐位相加、进位处理,以及如何在字符串/数组与结果字符串之间高效转换,是写出可靠代码的关键。

相关推荐
weixin_429630262 小时前
第6章 支持向量机
算法·机器学习·支持向量机
王哈哈^_^2 小时前
【数据集】【YOLO】【目标检测】共享单车数据集,共享单车识别数据集 3596 张,YOLO自行车识别算法实战训推教程。
人工智能·算法·yolo·目标检测·计算机视觉·视觉检测·毕业设计
Nan_Shu_6142 小时前
学习:JavaScript(5)
开发语言·javascript·学习
像风一样自由20202 小时前
Rust与Python完全指南:从零开始理解两门语言的区别与关系
开发语言·python·rust
stay_alive.2 小时前
C++ 四种类型转换
开发语言·c++
卡提西亚2 小时前
C++笔记-9-三目运算符和switch语句
c++·笔记
CodeWizard~3 小时前
AtCoder Beginner Contest 430赛后补题
c++·算法·图论
大大dxy大大3 小时前
机器学习-KNN算法示例
人工智能·算法·机器学习
喜欢吃燃面3 小时前
C++:哈希表
开发语言·c++·学习