加载中...

数组形式的整数加法(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];

分析:

  1. 看得出示例中数组的大小跟k之前是没有确定谁大谁小;
  2. 加法会产生进位;
  3. 结果应该不能保存在num中,我们需要自己开辟一个空间来足以存放下计算结果;
  4. 加法的规则:
    1. 逢十进一;
    2. 对应位数相加,各位加各位,十位加十位,.......;
  5. 函数返回值就是一个计算好的数组,同时再返回计算好数组的长度returnSize;
  6. 这里传的是一个数值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. 大数加法考察我们对于加法这种常见运算的规则掌握如何,我们应该要透过现象看本质,把他们变成一个个你熟悉的逻辑,就可以逐一攻破;
  2. 我们也要掌握通过特殊现象来推理出一般的情况,只有把问题考虑周全才有可能把它做好;
  3. 理想很美好,现实很残酷,只有不断的学习,锻炼自己的思维,才能活着更轻松;
  4. 掌握一定的矛盾分析法,也是一种终身受益的技能;

《减法如何实现?》

我们不玩虚的,直接开干!

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. 分析:

  1. 我们不知道两个数组谁大谁小,重新开辟一个num空间来存放减法计算出来的值;
  2. 减出来的数有负数;
  3. 可能会遇到不够减的情况,要考虑借位;
  4. 减法的规则:
    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. 总结

  1. 跟加法一样,需要用到减法的运算规则,把规则变成代码实现也是一种锻炼方式;
  2. 掌握个例,也是完成代码的一部分;

如果后续有思路,会继续更新一下乘法跟除法。就这样吧!如果你看完对你有所收获,那这篇文章还算凑合( •̀ ω •́ )y

更新
2022.3.10
15:32

posted @ 2022-03-10 15:32  一名博客  阅读(771)  评论(0编辑  收藏  举报