数组形式的整数加法(C语言)
不多BB, 直接开整,今天我们来学一下大数的加减法,看完对您应该有所收获。
大数顾名思义就是一个很庞大的数字,在各种语言中,系统自带的变量类型是无法存放这样的数字,所以我们需要自己想办法,将这样的数存起来,leetcode中有一道题就是如此,将起存放在一个数组当中。我们就先从一个题目开始讲起......
<题目>
整数的 数组形式 num 是按照从左到右的顺序表示其数字的数组。
例如,对于 num = 1321 ,数组形式是 [1,3,2,1] 。
给定 num ,整数的 数组形式 ,和整数 k ,返回 整数 num + k 的 数组形式 。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/add-to-array-form-of-integer
int* addToArrayForm(int* num, int numSize, int k, int* returnSize){
}
示例:加法
- num = [1,2,0,0], k = 34 =======> 结果就是一个[1,2,3,4];
- num = [9,9], k = 1 =======> 结果就是一个[1,0,0];
- num = [1,2], k = 12 =======> 结果就是一个[2,4];
- num = [1], k = 99 =======> 结果就是一个[1,0,0];
分析:
- 看得出示例中数组的大小跟k之前是没有确定谁大谁小;
- 加法会产生进位;
- 结果应该不能保存在num中,我们需要自己开辟一个空间来足以存放下计算结果;
- 加法的规则:
1. 逢十进一;
2. 对应位数相加,各位加各位,十位加十位,.......; - 函数返回值就是一个计算好的数组,同时再返回计算好数组的长度returnSize;
- 这里传的是一个数值k,在某种程度上也算是数组的形式,因为你可以一位一位的把k的个位,十位...分离出来然后放到一个数组里
<代码>
int* addToArrayForm(int* num, int numSize, int k, int* returnSize)
{
//按照上面分析一步一步走
//先计算一个k的长度
int k_count = 0;
int k_copy = k;
while (k_copy)//k_copy若为0则停止计算,测试用例:9;自行带入计算,k_count就是k的长度
{
k_count++;
k_copy /= 10;
}
int MaxLength = k_count > numSize ? k_count : numSize;
int* ReturnArray = (int*)malloc(sizeof(int) * (MaxLength + 1));//我们已经开辟好空间来存放加法后的数字了,这里加一是为了保证存储进位值
if(ReturnArray == NULL)//开辟失败,返回NULL指针
return NULL;
int add_length = MaxLength;
int next = 0;//进位信号
int Rcuri = MaxLength;//指向ReturnArray数组最后一个元素
int Acuri = numSize - 1;//指向num数组最后一个元素
while (add_length--)//显然这里MaxLength放这里只能处理MaxLength数据,而开辟的空间有MaxLength + 1,即ReturnArray指向的第一个数据我们并没有操作
{
int real_num = 0;
if(Acuri >= 0)//Acuri会越界的,如果数组的长度小于k的长度
real_num = num[Acuri];
ReturnArray[Rcuri] = next + real_num + k % 10;
//有没有小伙伴觉得这样写也可以的 :ReturnArray[Rcuri] = next + num[Acuri] + k % 10;这种写法就是没有发现num数组越界访问
if(ReturnArray[Rcuri] > 9)//当前位置产生了进位
{
ReturnArray[Rcuri] -=10;//减10就相当于next = 1
next = 1;
}
else
{
next = 0;
}
Rcuri--;
k /= 10;
Acuri--;
}
//循环结束之后我们还需要把第一个元素操作一下,但其实ReturnArray指向第一个元素要么是0要么是1,所以直接赋值next给第一个元素即可
ReturnArray[Rcuri] = next;
if(next)//next如果有进位,则表示ReturnArray指向的第一个元素是1,则按我们阅读习惯需要看到1;
{
*(returnSize) = MaxLength + 1;
return ReturnArray;
}
else//如果没有进位,则表示ReturnArray指向的第一个元素是0,则按我们阅读习惯不需要看到0;
{
*(returnSize) = MaxLength;
return (ReturnArray + 1);
}
}
总结
- 大数加法考察我们对于加法这种常见运算的规则掌握如何,我们应该要透过现象看本质,把他们变成一个个你熟悉的逻辑,就可以逐一攻破;
- 我们也要掌握通过特殊现象来推理出一般的情况,只有把问题考虑周全才有可能把它做好;
- 理想很美好,现实很残酷,只有不断的学习,锻炼自己的思维,才能活着更轻松;
- 掌握一定的矛盾分析法,也是一种终身受益的技能;
《减法如何实现?》
我们不玩虚的,直接开干!
1. 示例:减法
- num1 = [1,2,3,4], num2 = [3,4] =======> 结果就是一个[1,2,0,0];
- num1 = [9,8], num2 = [1,9] =======> 结果就是一个[7,9];
- num1 = [1,2], num2 = [1,2] =======> 结果就是一个[0,0];
- num1 = [1], num2 = [9,9] =======> 结果就是一个-[9,8];
1. 分析:
- 我们不知道两个数组谁大谁小,重新开辟一个num空间来存放减法计算出来的值;
- 减出来的数有负数;
- 可能会遇到不够减的情况,要考虑借位;
- 减法的规则:
1.不够减的位,向高位借一加十,再相减;
2.对应位相减,个位减个位,十位减十位,......;
1. <代码>
int* subToArrayForm(int* num1, int num1Size, int* num2, int num2Size, int* returnSize, int* SignBit)
{
//模拟num1 - num2,如何处理负数?返回一个符号位SignBit
int MaxLength = num1Size > num2Size ? num1Size : num2Size;//减出来最大的位数,就是按减数和被减数最大的那一个,测试用例:eg:100 - 0 = 100,要用三位
int* ReturnArray = (int*)malloc(sizeof(int) * MaxLength);//重新开辟一个数组存放计算好的值
if (ReturnArray == NULL)
{
return NULL;
}
int state = 1;//假设一开始就是两个数相等,就可以直接利用这个不出现负数的情况,直接走(num1 - num2)
if (num1Size < num2Size)
state = 0;//负数
else if (num1Size > num2Size)
state = 1;//正数
else //长度相等,判断一下谁大
{
for (int i = 0; i < MaxLength; i++)
{
if (!(num1[i] - num2[i]))
continue;//相同就继续
else if ((num1[i] - num2[i]) > 0)
{
state = 1;
break;
}
else if ((num1[i] - num2[i]) < 0)
{
state = 0;
break;
}
}
}
int borrow = 0;//借位信号
int Rcuri = MaxLength - 1;//指向ReturnArray最后一个元素
int Ncuri1 = num1Size - 1;//指向num1最后一个元素
int Ncuri2 = num2Size - 1;//指向num2最后一个元素
int sub_length = MaxLength;//控制循环
if (state == 0)
{
*(SignBit) = 1;//表示减出来的数是负数
//用num2 - num1
while (sub_length--)
{
int real_num1 = 0;
if (Ncuri1 >= 0)//num1会越界,测试用例:eg:传参过来的[1,2,3,4]-[9,9],实际上是要这么做才不会越界 ====> [1,2,3,4]-[0,0,9,9]
real_num1 = num1[Ncuri1];
*(ReturnArray + Rcuri) = num2[Ncuri2] - real_num1 - borrow;//当前位(例如:个位)做减法,不用考虑上一位(例如:十位),但要考虑上一位(例如:十位)的借位
if (*(ReturnArray + Rcuri) < 0)//不够减,借一
{
*(ReturnArray + Rcuri) += 10;
borrow = 1;
}
else
{
borrow = 0;
}
Rcuri--;
Ncuri1--;
Ncuri2--;
}
}
else if (state == 1)
{
*(SignBit) = 0;//表示减出来的数是正数
//用num1 - num2
while (sub_length--)
{
int real_num2 = 0;
if (Ncuri2 >= 0)//num2会越界,测试用例:eg:传参过来的[1,2,3,4]-[9,9],实际上是要这么做才不会越界 ====> [1,2,3,4]-[0,0,9,9]
real_num2 = num2[Ncuri2];
*(ReturnArray + Rcuri) = num1[Ncuri1] - real_num2 - borrow;//当前位(例如:个位)做减法,不用考虑上一位(例如:十位),但要考虑上一位(例如:十位)的借位
if (*(ReturnArray + Rcuri) < 0)//不够减,借一
{
*(ReturnArray + Rcuri) += 10;
borrow = 1;
}
else
{
borrow = 0;
}
Rcuri--;
Ncuri1--;
Ncuri2--;
}
}
*(returnSize) = MaxLength;//直接返回MaxLength即可,因为减出来最大的位数,就是按减数和被减数最大的那一个
return ReturnArray;//在外面free一下
}
1. 总结
- 跟加法一样,需要用到减法的运算规则,把规则变成代码实现也是一种锻炼方式;
- 掌握个例,也是完成代码的一部分;
如果后续有思路,会继续更新一下乘法跟除法。就这样吧!如果你看完对你有所收获,那这篇文章还算凑合( •̀ ω •́ )y
更新
2022.3.10
15:32