高精度减法

两个高精度整数的减法

高精度指的是数字的大小非常非常大,最多能有10的5次方 的 位数。

本次计算的两个数均为 正数,如何求负数会在最后提到。

题目

给定两个正整数(不含前导 0 0 0),计算它们的差,计算结果可能为负数。

输入格式

共两行,每行包含一个整数。

输出格式

共一行,包含所求的差。

数据范围

1 ≤ 整数长度 ≤ 1 0 5 1 \le 整数长度 \le 10^5 1整数长度105

输入样例:

32
11

输出样例:

21

思路实现

我们先来模拟一下人类是怎么实现减法的。

在这里插入图片描述
首先 先计算 5 - 6,由于不够减,那么就需要 向上借一位。

然后就变成了 15 - 6,等于9。
在这里插入图片描述
接着计算 7 - 9,由于上一位 借了一位,所以应该是 6 - 9。

由于又不够借,所以再向上借一位。

16 - 9 = 7
在这里插入图片描述
最后 就是 11 - 5,所以最终答案是 679。

其实高精度减法 的 思路 是跟我们的减法是一模一样的。

代码实现

接着来看代码。

跟高精度加法一样,由于没有一个数据类型能够存储这么大的数字,所以需要用到数组存储.

由于vector容器不用手动 记录数组当前有效数据的个数,所以我们采用 vector 容器(后面也会有数组的代码,相较于vector,数组的优点是 比vector 会在时间上 快很多,vector的优点是 代码简单)。

在这里插入图片描述
这个字符串类型 是用于 读入 很大的数字的,因为整数类型存不下。

在这里插入图片描述
用字符串读入,然后遍历字符串,将每一位数字,存到数组里,这里的数字是倒着存的,原因我们后面在说。

注意这里的字符串遍历的 每一位都是 字符,所以一定要减去 ‘0’(字符0)。

接着我们需要注意一个问题,虽然读入的都是正数,但是 相减的操作是有可能 产生负数的。

根据人类减法的习惯,如果是一个小数字减去一个大数字,那么我们在脑子当中 计算的过程是,先大数字减去小数字 然后再带上负号。

所以这个时候就需要判断 A这个数字大,还是B这个数字大。

在这里插入图片描述
如果 A 大于等于B ,那么则正常计算。
在这里插入图片描述

如果 B 比较大,那么则计算 B - A,然后打印的 时候 多打印个 符号 即可。注意这里等于的情况是不需要打印 符号的,要不然结果会是 - 0。


接着我们来实现 compare 方法和 sub 方法。

对于compare方法来说,需要判断 A 数组里面存的数字大还是 B数组里面存的数字大。
在这里插入图片描述
规定 返回true 的时候 是 A大于等于 B ,返回 false 的时候是 B 大于 A。

注意:这里 等于这种情况一定 是 true ,因为如果是false 的 话 就会出现 -0的情况。 0是不需要加负号的。

在这里插入图片描述
首先 先看他们的数组长度,如果不一样,那么长度长的 一定大。

接着就开始从最高位比较,由于数组是倒着存的,那么最高位在数组的末尾。

在这里插入图片描述
其中 由于 此时的 A 的长度一定等于 B 的长度,所以 for循环 i 的初始值是谁的长度都是一样的。

从最高位开始比,如果两个值 不一样,那么大的整个数字 就一定大。

如果for 循环走完了,那么就说明 两个数字相等了,根据之前我们说的 ,等于一定是返回 true 的,要不然 就会打印 -0 .
在这里插入图片描述
当然这段代码还可以进行小优化。

在这里插入图片描述
这里的等号可以去掉,因为一定不可能出现等于的情况。

在这里插入图片描述


接着我们来看 sub 函数

首先函数的外壳长这个样子

在这里插入图片描述
由于 在main函数里面的 操作,这里 A的大小一定 是比 B的大。

接着需要定义一个变量来存储 上一位的借位。

在这里插入图片描述
这个 t 的 含义是 上一位 欠了几,比如 如果 5- 9 ,那么就叫做 欠了-1。

在这里插入图片描述
让这个 t 加上 被减数,然后如果 B 此时的位数有数字的话,减去 B这个数字。

接下来,就需要将结果放到 另一个vector容器中,到这里我们还没定义,得补上。

此时有两种情况,第一种是 t < 0,此时 就需要借一位了,就需要插入 (t + 10),另一种是 t > 0,此时不需要借位,直接插入 t 就可以了。

其实这两种情况也可以弄成一种情况。

在这里插入图片描述
如果插入 (t + 10) % 10,此时这两种情况就会都满足。

接着 再根据 t 的值,给出 这一位 欠多少。

在这里插入图片描述
如果小于 0 则代表,借了个1。大于0 则代表没有借。

最终经过一次遍历,得到的便是 两数相减的结果。

只不过此时还没有结束,我们少考虑了一个东西。

比如 是下面两个数 相减

在这里插入图片描述
那么就会得到 0025,所以我们需要把前面的0都给消去。

在这里插入图片描述
需要满足两个条件才抹去 最高位,第一个是 剩下的位数必须大于1,可不敢答案本来是 0,然后给他抹去了。

第二个是 你的最高位为0。

由于在数组后端移除数字比较方便,所以也解释了为什么数字在数组当中是倒着存的,当然也有一个原因是因为,要跟我们的高进度加法保持一致,这样组合运算的时候会方便很多。

最后,返回 vector容器 C即可。
在这里插入图片描述

完整代码如下:

#include <iostream>
#include <vector>
using namespace std;

bool compare(vector<int>& A, vector<int>& B)
{
    if (A.size() != B.size()) return A.size() > B.size();
    
    for (int i = A.size()-1; i >= 0; i--)
    {
        if (A[i] != B[i]) return A[i] > B[i];
    }
    return true;
}

vector<int> sub(vector<int>& A, vector<int>& B)
{
    vector<int> C;
    int t = 0;
    for (int i = 0; i < A.size(); i++)
    {
        t += A[i];
        if (i < B.size()) t -= B[i];
        C.push_back((t + 10) % 10);
        if (t < 0) t = -1;
        else t = 0;
    }
    
    while (C.size() > 1 && C.back() == 0) C.pop_back();
    return C;
}


int main()
{
    
    string a, b;
    vector<int> A, B;
    cin >> a >> b;
    for (int i = a.size()-1; i >= 0; i--) A.push_back(a[i] - '0');
    for (int i = b.size()-1; i >= 0; i--) B.push_back(b[i] - '0');
    
    if (compare(A, B))
    {
        auto C = sub(A, B);
        for (int i = C.size()-1; i >= 0; i--) cout << C[i];
    }
    else
    {
        auto C = sub(B, A);
        cout << '-';
        for (int i = C.size()-1; i >= 0; i--) cout << C[i];
    }
    
    return 0;
}

数组代码如下:

#include <iostream>
using namespace std;

const int N = 1e5+10;

string a, b;
int A[N], B[N], C[N], asz, bsz, csz;

bool compare(int a[], int b[])
{
    if (asz != bsz) return asz > bsz;
    
    for (int i = asz-1; i >= 0; i--)
    {
        if (a[i] != b[i]) return a[i] > b[i];
    }
    return true;
}

void sub(int a[], int b[])
{
    int t = 0;
    for (int i = 0; i < asz; i++)
    {
        t += a[i];
        if (i < bsz) t -= b[i];
        C[csz++] = (t + 10) % 10;
        if (t < 0) t = -1;
        else t = 0;
    }
    
    while (csz > 1 && C[csz-1] == 0) csz--;
}


int main()
{
    cin >> a >> b;
    for (int i = a.size()-1; i >= 0; i--) A[asz++] = a[i] - '0';
    for (int i = b.size()-1; i >= 0; i--) B[bsz++] = b[i] - '0';
    
    if (compare(A, B))
    {
        sub(A, B);
        for (int i = csz-1; i >= 0; i--) cout << C[i];
    }
    else
    {
        swap(asz, bsz);
        sub(B, A);
        cout << '-';
        for (int i = csz-1; i >= 0; i--) cout << C[i];
    }
    return 0;
}

易错点:注意 数组的代码 中 如果 结果是负数,那么就一定要交换 asz 和 bsz 的值。

两个任意符号的高精度加减法

接下来我们来分析一下,如果题目当中给的数字 有负数怎么办,其实很好解决。

对于所有的问题 我们都可以把他们分为两种情况。
在这里插入图片描述
一种 是A 的绝对值加上B 的绝对值,另一种是 A的绝对值减去 B 的绝对值.

比如 -5 - 10 就可以转化为 - (5 + 10);
-5 + 10 就可以转化为 10 - 5 …

所以只需知道每个数是正的还是负的就可以了,我们在读取数字的时候是用 字符串读取的,所以是不是负数,我们只需要判断第一个字母是不是负号就可以了。

最后再根据 两个数的正负和 进行加或减的操作分类 即可。


  • 35
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值