1 大数运算
介绍
大数运算,顾名思义,就是很大的数值的数进行一系列的运算。
我们知道,在数学中,数值的大小是没有上限的,但是在计算机中,由于字长的限制,计算机所能表示的范围是有限的,当我们对比较小的数进行运算时,如:1234+5678,这样的数值并没有超出计算机的表示范围,所以可以运算。但是当我们在实际的应用中进行大量的数据处理时,会发现参与运算的数往往超过计算机的基本数据类型的表示范围,比如说,在天文学上,如果一个星球距离我们为100万光年,那么我们将其化简为公里,或者是米的时候,我们会发现这是一个很大的数。这样计算机将无法对其进行直接计算。
可能我们认为实际应用中的大数也不过就是几百位而已,实际上,在某些领域里,甚至可能出现几百万位的数据进行运算,这是我们很难想象的。如果没有计算机,那么计算效率可想而知。
既然在计算机中无法直接表示,那么大数到底如何进行运算呢,学习过数据结构的都知道线性表,将大数拆分然后存储在线性表中,不失为一个很好的办法。
原理
利用数组连续性,将大数每一位上的数字单独取出放入对应的数组格中,然后再对每一位做单独的加减乘运算。形象的说,类似于小学学习加减乘所列的式子。
2 题目描述
整数的 数组形式
num
是按照从左到右的顺序表示其数字的数组。示例 1:
输入:num = [1,2,0,0], k = 34
输出:[1,2,3,4]
解释:1200 + 34 = 1234示例 2:
输入:num = [2,7,4], k = 181
输出:[4,5,5]
解释:274 + 181 = 455示例 3:
输入:num = [2,1,5], k = 806
输出:[1,0,2,1]
解释:215 + 806 = 1021
3 解题思路
通过以上对大数运算的了解之后,知道了大数运算实质上是利用每一位对应数组位置的数字单独进行加减乘运算然后通过进位的方式进行运算得出结果。
通过对题目的分析,我们可以知道:
①我们需要对k做类似的调整,让k的每一位对应数组的每一位进行加运算。
②需要一个进位标志,记录加运算后是否需要进位。
③个位数相加最大为9+9=18,进位最大为1,最小为0,如果有进位需要拿到和的最低位需要减去10。
④三位数加三位数的和不会超过四位数,三位数加四位数不会超过五位数。
综上分析,我们可以得到以下基本解题思路。
①我们需要对k做类似的调整,让k的每一位对应数组的每一位进行加运算。
我们可以选择将k的每一位拆下来存入数组中对应相加,也可以从低位开始每拆一个再与数组对应位相加。
通过这样我们可以得到k的每一位。
其次我们需要一个新的数组,这个数组肯定比k的位数和原数组空间更大,便于去存取每一次相加后得到的值。
进行5+4时候无进位。
8+3=11,进位cab=1,表示需要进位,进位数位1。
num中的2再加进位的1得到三,之后再把cab设为0。
最后得到结果1319。
到这里,我们基本上清楚了该以何种逻辑去写这个代码,但是,聪明的人可能就已经发现了一个小问题,新得到的数组他可能没有装满整个数组呀,而且最前面还空着,并不方便我们得到这个答案呀,我们还需要去判断整个数组是不是装满了,如果没有的话还需要把其他位置的数字全部往前面挪动,想到这是不是感觉还挺麻烦的对吧。那有没有什么更简单的方法呢?当然是有的!
我们回到最开始计算的地方。
如果我们将低位的计算结果顺序存储到数组中去呢,会是怎样的一个结果呢?
如下
有没有发现最后的答案倒过来了呀,我们是不是直接把这个数组内的元素逆置一下不就行了?
到此为止,这个题就算结束了。
另外,里面的另一种情形(数组元素个数小于k的位数):
当各取最低位时,数组先取完,不附加条件就会导致越界的问题,如果num从末尾位置取到0结束之后,k还在取,则将num中取的数字设为0。
4 完整代码+注释
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int* addToArrayForm(int* num, int numSize, int k, int* returnSize) {
int cab=0;//进位
int knum=k;
int ksize=0;
//统计k的位数
while(knum)
{
ksize++;
knum/=10;
}
int len=numSize>ksize?numSize:ksize;//得到位数最多的
int *cal=(int*)malloc(sizeof(int)*(len+1));//开辟len+1个内存去存结果
int numi=numSize-1; //数组末位置下标
int calsize=0;//新数组元素个数
while(len--)
{
int a=0;
if(numi>=0) //防止数组元素比k位数小导致越界访问
{
a=num[numi];
numi--;
}
cal[calsize]=cab+k%10+a;//进位+k的低位+num数组的对应元素
if(cal[calsize]>9) //和大于9则进位
{
cal[calsize]=cal[calsize]-10; //得到个位
cab=1; //进位为1
}
else{
cab=0;
}
calsize++;
k=k/10;
}
if(cab==1)//每位数相加后如果还存在进位,则在后面再补一个1
{
cal[calsize]=1;
}
else//如果没有进位,则减去多出来的一个进位空
{
calsize--;
}
//反转数组元素
int*sta=cal;
int*end=cal+calsize;
while(sta<end)
{
int t=*sta;
*sta=*end;
*end=t;
sta++;
end--;
}
*returnSize=calsize+1;
return cal;
}