Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【C++经典例题】大数相加:从基础实现到性能优化

【C++经典例题】大数相加:从基础实现到性能优化

作者头像
倔强的石头_
发布于 2025-02-21 01:23:55
发布于 2025-02-21 01:23:55
11100
代码可运行
举报
文章被收录于专栏:C/C++指南C/C++指南
运行总次数:0
代码可运行

问题背景

在实际编程中,我们常常会遇到需要处理大整数相加的情况。由于编程语言中基本数据类型所能表示的整数范围有限,当需要处理的整数超出这个范围时,我们就不能直接使用基本数据类型进行计算。 此时,我们可以将大整数以字符串的形式存储,通过逐位相加的方式来实现大整数的加法运算。 本文将围绕这个问题,详细介绍三种不同实现方式,从基础版逐步优化到性能更优的版本。

基础版实现

代码示例
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Solution {
public:
    string addStrings(string num1, string num2) 
    {
        int end1 = num1.size() - 1;;//从字符串尾部遍历
        int end2 = num2.size() - 1;

        int next = 0;//进位
        string s;

        while (end1 >= 0 || end2 >= 0 )//两个字符串都遍历完成才结束
        {
            int n1 = end1 >= 0 ? num1[end1--] - '0' : 0;//先判断下标是否合法,防止越界
            int n2 = end2 >= 0 ? num2[end2--] - '0' : 0;

            int ret = n1 + n2 + next;
            next = ret / 10;
            ret = ret % 10;

            s.insert(0, 1, ret + '0');

        }

        if (next == 1)
        {
            s.insert(0, 1, '1');
        }
        return s;
    }
};
实现思路
  1. 初始化:首先,我们需要找到两个字符串的末尾位置 end1end2,从这里开始逐位相加。同时,我们还需要一个变量 next 来记录进位,初始值为 0。另外,我们创建一个空字符串 s 用于存储相加的结果。
  2. 逐位相加:使用 while 循环,只要两个字符串中有一个还未遍历完,就继续循环。在每次循环中,我们先判断当前下标是否合法,如果合法则取出对应位置的数字,否则将其视为 0。然后将这两个数字与进位 next 相加,得到当前位的和 ret。接着,更新进位 nextret 除以 10 的结果,ret 则取 ret 对 10 取模的结果。最后,将 ret 转换为字符并插入到字符串 s 的开头。
  3. 处理最后进位:循环结束后,我们需要检查是否还有进位,如果有则将其插入到字符串 s 的开头。
存在的问题

这种实现方式的时间复杂度较高,主要是因为在字符串 s 的开头插入字符的操作会导致字符串元素的移动,时间复杂度为O(n) ,其中 n是字符串的长度。因此,总的时间复杂度为 O(n²)。

尾插优化版

代码示例
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Solution {
public:
    string addStrings(string num1, string num2)
    {
        int end1 = num1.size() - 1;;//从字符串尾部遍历
        int end2 = num2.size() - 1;

        int next = 0;//进位
        string s;

        while (end1 >= 0 || end2 >= 0)//两个字符串都遍历完成才结束
        {
            int n1 = end1 >= 0 ? num1[end1--] - '0' : 0;//先判断下标是否合法,防止越界
            int n2 = end2 >= 0 ? num2[end2--] - '0' : 0;

            int ret = n1 + n2 + next;
            next = ret / 10;
            ret = ret % 10;

            s += ret + '0';//先尾插

        }

        if (next!=0)//防止丢失最后进位的数
        {
            s += next + '0';
        }
        
        reverse(s.begin(), s.end());//再反转
        return s;
    }
};
优化思路

为了避免在字符串开头插入字符带来的性能开销,我们可以采用尾插的方式:

在每次循环中,将当前位的结果字符直接添加到字符串 s 的末尾,这样时间复杂度为O(n) 。循环结束后,由于结果是从低位到高位添加到字符串中的,所以我们需要将字符串反转,得到正确的顺序。

复杂度分析

这种实现方式的时间复杂度为 O(n),其中 是两个字符串中较长的那个的长度。因为我们只需要遍历一次字符串,并且尾插和反转操作的时间复杂度都是O(n) 。

扩容优化版

代码示例
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Solution {
public:
    string addStrings(string num1, string num2)
    {
        int end1 = num1.size() - 1;;//从字符串尾部遍历
        int end2 = num2.size() - 1;

        int next = 0;//进位

        string s;
        s.reserve(max(num1.size(), num2.size()) + 1);//直接一次扩容完成

        while (end1 >= 0 || end2 >= 0)//两个字符串都遍历完成才结束
        {
            int n1 = end1 >= 0 ? num1[end1--] - '0' : 0;//先判断下标是否合法,防止越界
            int n2 = end2 >= 0 ? num2[end2--] - '0' : 0;

            int ret = n1 + n2 + next;
            next = ret / 10;
            ret = ret % 10;

            s += ret + '0';//先尾插

        }

        if (next!=0)//防止丢失最后进位的数
        {
            s += next + '0';
        }
        
        reverse(s.begin(), s.end());//再反转
        return s;
    }
};
优化思路

在尾插优化版的基础上,我们进一步考虑字符串扩容的问题。

当我们使用 += 操作向字符串中添加字符时,如果字符串的容量不足,会自动进行扩容操作,而扩容操作会涉及到内存的重新分配和数据的复制,这会带来一定的性能开销。

为了避免这种情况,我们可以在开始时就使用 reserve 方法为字符串预留足够的空间,这样就可以避免多次扩容。

复杂度分析

这种实现方式的时间复杂度仍然为O(n) ,但由于减少了扩容操作,实际运行时间会更短,性能更优。

总结

通过对大数相加问题的三种不同实现方式的分析,我们可以看到,从基础版到尾插优化版再到扩容优化版,性能逐步提升。在实际编程中,我们应该根据具体情况选择合适的实现方式,同时要注意代码的性能优化,避免不必要的开销。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-02-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【C++】string OJ练习
所以字符串中字符的范围就是【a,z】,那我们就可以创建一个大小为26的整型数组,然后用一个相对映射去统计每个字母的出现次数,a就映射到下标为0的位置,b就映射到下标为1的位置,依次类推。 那怎么让这些字母映射到对应的位置呢? 减去’a’得到的值是不是就是它们映射的位置啊,然后遍历字符串,每个字母映射的值是几,就让下标为几的元素++,初值全为0,这样遍历过后每个字母出现的次数就统计出来了。(下标0的元素的值就是a出现的次数,1位置就是b出现的次数…) 但是现在有一个问题,那就是出现一次的字母可能不止一个,我们怎么判断那个是第一个只出现一次的字母呢? 🆗,这里我们不要去遍历统计次数的数组,还是从前往后去遍历字符串,然后看哪个字母的次数是1,第一个是1的就是第一个只出现一次的字母。
YIN_尹
2024/01/23
1540
【C++】string OJ练习
string类练习题
本篇博客主要记录string类的相关oj题,后续会持续更新,题目为入门基础题,目的是帮助初学string类的友友们熟悉使用string类. 题目包含:字符串最后一个单词的长度、 2.反转字符串 II、字符串相加
初阶牛
2023/10/14
2410
string类练习题
【Leetcode】string类刷题
接着,创建两个索引,begin和end,一个从前往后找,找到一个字母停止,另一个从后面找,找到字母停止,然后进行交换,保证begin<end,比较简单,代码如下:
用户11029103
2024/04/20
1200
【Leetcode】string类刷题
【C++修行之道】string类练习题
如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。
走在努力路上的自己
2024/07/13
1520
【C++修行之道】string类练习题
【C++】STL容器——string类的例题应用(9)
YY的秘密代码小屋
2024/01/22
1750
【C++】STL容器——string类的例题应用(9)
【C++】string的9道OJ题
1. 这道题的难度算非常简单的了,我们可以定义两个变量来表示数组首尾位置的有效字符的下标,然后分别从前和从后向中间遍历,只要遇到字母就停下来,利用库函数swap进行交换。
举杯邀明月
2023/04/12
4340
【C++】string的9道OJ题
【C++】 string类:应用与实践
💥个人主页:大耳朵土土垚的博客 💥 所属专栏:C++入门至进阶 这里将会不定期更新有关C++的内容,希望大家多多点赞关注收藏💖💖
大耳朵土土垚
2024/05/24
1550
【C++】 string类:应用与实践
【OJ】string类题目
再定义一个变量用来记录进位int next=0;。重新定义一个string的字符串用来记录相加结果string retstr。 当两个字符相加,得注意,要转换为整形相加,当ret大于10,那么next=ret/10,而字符穿要记录的是ret=ret%10,再头插retstr.insert(0,1,ret+'0'),头插时要转换为字符头插。 如果9+1,那么返回结果就是0,所以不能忘记少插入进位值,在最后判断一下,再进行头插:
zxctscl
2024/03/17
990
【OJ】string类题目
【C++】9道经典面试题带你玩转string类
https://leetcode.cn/problems/ba-zi-fu-chuan-zhuan-huan-cheng-zheng-shu-lcof/
修修修也
2024/06/17
1270
【C++】9道经典面试题带你玩转string类
OJ习题 篇1
有时候我们需要非常大的数据相加时,整型的范围不够,就可以将数据转换为字符串的形式运算,再将结果转回为整型。 整型相加时是从后往前加的,这里的字符串相加我们也从后往前加。不断取出两个字符串的末尾字符,转换为整形后相加,再用+=追加到字符串末尾,其中还要考虑进位的情况。 因为string类支持operator[],所以我们可以通过下标的方式遍历字符串。 其中两个字符串的第一位相加也可能有进位,所以循环结束后还需要判断进位是否为1。 因为我们是从后往前加的,所以最后还需要用reverse将字符串翻转过来。
_小羊_
2024/10/16
840
OJ习题 篇1
【OJ】string类刷题
题目给的是到2k就翻转前k个,那么循环的时候直接跳到2k处就行for (int i = 0; i < n; i += 2 * k) 。 这里用 reverse翻转的时候区间选择与k有关,以例1为例:发现第一个翻转区间是[0,2),也就从i=0到i+k,得注意区间是左闭右开的;第二个翻转区间是[4,6),也就是i=4到n, reverse的结束就得取i+k和n中小的那一个。将区间写出来就是s.begin() + i, s.begin() + min(i + k, n)。
zxctscl
2024/03/21
740
【OJ】string类刷题
【C++经典例题】基于字符串实现大数相乘问题
在实际编程中,我们经常会遇到需要处理大整数的情况。由于编程语言中内置整数类型(如 int、long 等)有其表示范围的限制,当需要处理的整数超出这些范围时,就不能直接使用内置类型进行计算。
倔强的石头_
2025/04/15
1490
【C++经典例题】基于字符串实现大数相乘问题
【深度剖析 C++11】 第一弹:现代 C++ 编程的基石与革新
C++11后,意在统一初始化方式,做到一切对象都可以用{ }来初始化,也叫做列表初始化,内置类型支持,自定义类型也支持。代码中出现的移动构造我们后面会为大家仔细讲解
换一颗红豆
2024/12/20
790
【深度剖析 C++11】 第一弹:现代 C++ 编程的基石与革新
Leetcode之string
题目思路: 本题为大数运算类型题目, 不能用于处理大整数的库, 但可以使用一般的算术运算, 我们进行模拟, 首先依次取出每个数字的最后一位,进行加法运算, 并且将值分为进位和数值, 第一次的进位next = 0 , 这里只有当num1和num2都结束才能结束循环, 例如下面999999999+1, 如果其中一个数字已经结束, 则在高位补0, 并且将每一次的值追加到答案字符串, 循环结束, 如果进位还有值, 也追加到结束字符串, 最后逆置字符串.
用户11317877
2024/10/16
840
Leetcode之string
c++:string相关的oj题(415. 字符串相加、125. 验证回文串、541. 反转字符串 II、557. 反转字符串中的单词 III)
利用每次要跳2k来处理:就直接i+=2k,这样每次直接跳到下一个区间,前面够2k的不用管,直接满足i+k<=len,只有那最后一个不够2k的需要讨论(毕竟s.begin()+len是最后元素的下个位置)
是Nero哦
2024/01/24
1710
c++:string相关的oj题(415. 字符串相加、125. 验证回文串、541. 反转字符串 II、557. 反转字符串中的单词 III)
C++刷题(三):string
将字符串数字转换成对应整型的方法即:用字符数字的ASCII-‘0’,这道题的难点在于,如何判断边界,处理溢出问题。我们必须在计算前就判断是否越界,因为不管是否储存了表达式的结果,如果计算发生越界,则返回的就是溢出后的数据(即数据不是真实的计算结果,无法按照正常逻辑比较数字大小)。
用户11029137
2025/03/19
500
C++刷题(三):string
(c++实现)leetcode给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和
你不能使用任何內建 BigInteger 库, 也不能直接将输入的字符串转换为整数形式
艳艳代码杂货店
2021/09/19
1.1K0
【刷题】Leetcode 415 字符串相加 和 34 字符串相乘
本题我们只需要对两个大整数模拟「竖式加法」的过程。竖式加法就是我们平常学习生活中常用的对两个整数相加的方法,回想一下我们在纸上对两个整数相加的操作,是不是将相同数位对齐,从低到高逐位相加,如果当前位和超过 10,则向高位进一位?因此我们只要将这个过程用代码写出来即可。
叫我龙翔
2024/03/11
1600
【刷题】Leetcode 415 字符串相加 和 34 字符串相乘
算法思想总结:字符串
思路1:两两比较 时间复杂度mn 实现findcomon返回两两比较后的公共前缀
小陈在拼命
2024/07/16
1040
算法思想总结:字符串
[C++]string的使用
1.string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits和allocator作为basic_string的默认参数。
IT编程爱好者
2023/05/11
5180
[C++]string的使用
相关推荐
【C++】string OJ练习
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验