前言
本博客是保姆级的大数加法思路剖析,带你使用大数加法
的思路,AC以下三道题:415. 字符串相加
、LCR 025. 两数相加 II
和43. 字符串相乘
,由于限制了用一种思路去解三道题。看完这篇博客之后,可以帮助你激发解决大数四则运算的思路。
什么是大数加法
大数加法
主要解决的是两个非常大的数(超过了基本数据类型能表示的范围)相加的问题。在计算机中,基本数据类型(如int、long等)有固定的位数,因此它们能表示的数值范围是有限的。当需要处理的数值超过了这个范围时,就需要使用特殊的算法来实现大数的加法。即使用string
、list
等容器存储数字,再模拟竖式计算的过程,得到最终的答案。
415. 字符串相加 - 力扣(LeetCode)
这道题是大数加法的模版题,AC思路就是上文提到过的模拟竖式计算,整个过程分为两步:
基本循环
如图所示,在没有进位的情况下,分别遍历两个字符串,将同位的数字相加,得数便是得数该位的结果。
char
类型与int
类型的数字转换公式:
- int型 = char型 - ‘0’
- char型 = int型 + ‘0’
处理进位
我们会遇到两个地方要对进位进行处理
- 当基本循环的两个同位数之和大于10,就会进位到下一位。
- 上一位的进位要加入到基本循环中。
所以得数的某一位的公式可以总结为:str1对应位 + str2对应位 + 进位 = 该位得数,然后再用一个变量存储进位即可。记得得数最后一位也要检查进位变量
string addStrings(string num1, string num2) {
//从低位开始遍历
int i1=num1.size()-1,i2=num2.size()-1;
string res;
int next=0;
while(i1>=0 || i2>=0){
int temp=0,n1=-1,n2=-1;
if(i1==-1){
n1=0;
}
else{
n1=num1[i1]-'0';
i1--;
}
if(i2==-1){
n2=0;
}
else{
n2=num2[i2]-'0';
i2--;
}
//每一位的组成为:
//num1对应位+num2对应位+进位
temp=n1+n2+next;
char ch=temp%10+'0';
next=temp/10;
res+=ch;
}
` //检查进位变量
if(next)
res+='1';
reverse(res.begin(),res.end());
return res;
}
LCR 025. 两数相加 II - 力扣(LeetCode)
有了大数加法的基础,对于这样类似竖式计算的题,很容易就能有思路:遍历链表,将链表转为用string表示,然后直接用大数加法计算得出结果,最后将结果再转为链表。(虽然这个方法不是最优解,但如果用STL库中的迭代器区间构造,代码量是比较少的)
参考代码:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode* cur1 = l1;
ListNode* cur2 = l2;
string str1;
string str2;
while (cur1) {
str1 += cur1->val + '0';
cur1 = cur1->next;
}
while (cur2) {
str2 += cur2->val + '0';
cur2 = cur2->next;
}
string temp = addString(str1, str2); //addstring就是刚才的字符串加法
ListNode* ans = new ListNode(temp[0] - '0');
ListNode* cur = ans;
for (int i = 1; i < temp.size(); i++) {
int num = temp[i] - '0';
ListNode* Node = new ListNode(num);
cur->next = Node;
cur = cur->next;
}
return ans;
}
当然,我觉得反转链表后,再模拟竖式计算是最优解,少了官方解答或是大数加法的stack或string的额外空间消耗。可以自己尝试实现一下!
43. 字符串相乘 - 力扣(LeetCode)
如果想用大数加法的思路解决这道题,就要想起乘法的本质:“n*m”表示n个m相加的和。这样就将乘法过程转化为了加法过程,我们就能直接用大数加法的接口去解决这道题:
string multiply(string num1, string num2) {
if(num1=="0"||num2=="0")
return "0";
string res,cot("0");
while(cot != num1){
cout<<cot<<endl;
cot=addStrings(cot,"1");
res=addStrings(res,num2);
}
return res;
}
当然,这样做是会超时的,因为“n个”的n是大数,进行n次相加必然超时,所以需要优化我们的代码。
我们还是拆解为n个数相加,但尝试模拟实现竖式乘法的过程,我们让n变为另一个数字的一位,那么n将小于等于9。结果处再补0即可。
这样优化之后,我们最多进行200*9次大数加法,便能得出最终的结果。
参考代码(最优解还是得完全用乘法模拟过程,但这里就不给出参考代码了):
string multiply(string num1, string num2) {
if(num1=="0"||num2=="0")
return "0";
int i1=num1.size()-1,i2=num2.size()-1;
string res;
for(int i=0;i<num2.size();i++){
int n=num2[num2.size()-i-1]-'0';
string temp=_addstirng(num1,n);
for(int j=i;j>0;j--){
//补足最后的‘0’
temp.push_back('0');
}
res=addStrings(res,temp);
}
return res;
}
string _addstirng(string num1, int n){
string res;
while(n){
res=addStrings(res,num1); //addString为模版的大数加法
n--;
}
return res;
}
谢谢你的阅读!