欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 汽车 > 新车 > 【C++经典例题】大数相加:从基础实现到性能优化

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

2025/2/24 7:28:14 来源:https://blog.csdn.net/2302_78391795/article/details/145742203  浏览:    关键词:【C++经典例题】大数相加:从基础实现到性能优化

           💓 博客主页:倔强的石头的CSDN主页 

           📝Gitee主页:倔强的石头的gitee主页

            ⏩ 文章专栏:C++经典例题

                                  期待您的关注

1b7335aca73b41609b7f05d1d366f476.gif

目录

问题背景

基础版实现

代码示例

实现思路

存在的问题

尾插优化版

代码示例

优化思路

复杂度分析

扩容优化版

代码示例

优化思路

复杂度分析

总结


 

问题背景

在实际编程中,我们常常会遇到需要处理大整数相加的情况。由于编程语言中基本数据类型所能表示的整数范围有限,当需要处理的整数超出这个范围时,我们就不能直接使用基本数据类型进行计算。

此时,我们可以将大整数以字符串的形式存储,通过逐位相加的方式来实现大整数的加法运算

本文将围绕这个问题,详细介绍三种不同实现方式,从基础版逐步优化到性能更优的版本。

 

基础版实现

代码示例

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

 

存在的问题

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

 

 

尾插优化版

代码示例

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) 。

 

扩容优化版

代码示例

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) ,但由于减少了扩容操作,实际运行时间会更短,性能更优。

 

总结

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

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词