神机百炼1.8-高精度减法

大数减法导图

食用指南:

对该算法程序编写以及踩坑点很熟悉的同学可以直接跳转到代码模板查看完整代码
只有基础算法的题目会有关于该算法的原理,实现步骤,代码注意点,代码模板,代码误区的讲解
非基础算法的题目只有题目分析,代码实现,代码误区

题目描述:

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

    输入格式
    共两行,每行包含一个整数。

    输出格式
    共一行,包含所求的差。

    数据范围
    1≤整数长度≤10^5
    输入样例:
    32
    11
    输出样例:
    21

  • https://www.acwing.com/problem/content/794/

题目分析:

  • 不含前导0:其实这是个提示,减法的结果经常出现前导0,我们需要去除前导零
    如100 - 99 == 1,而不是001
  • 整数长度:最大10^5,必然是大数高精度运算了
    回顾一下常见的数据大小:
    char最大127,3位
    short最大65535,5位
    int最大2,147,483,647,10位
    long long最大9,223,372,036,854,775,807 19位

算法原理:

大数:

  • 高精度问题只针对整型,不产生小数
  • 所有含有负数的运算都可以改为绝对值运算,结果加正负号,所以手动实现的高精度运算不考虑负数
  • 大数:数字的位数超过百万的数(PS:百万本身就7位数)
  • 高精度问题其实是两个问题:
  1. 大数的输入输出存储形式
  2. 大数四则运算函数

大数的存储:

  • 示意图:大数存储
  1. 以int数组存储大数:不推荐,你可能申请不到这么大的连续内存空间
  2. 以string存储大数:不推荐,每次运算需要提前-‘0’
  3. 以vector存储大数:推荐
  • “字节序”:
    arr[0]存储个位上数值,arr[n]存储权值为10^n的数值
    优点:arr[n++]即可表示进位,同理vec.push_back()则vec.size()增加1,表示进位
  • 输入大数:
    先以string接收到字符串,再从string.size()-1逆序输出到0,每个元素-'0’后写入vector<int>
  • 输出大数:
    由于输出要求高位在前,而存储高位在后
    需要逆序输出,从vector.size()-1输出到0
  • 只需要记住:大数的输入经过一次逆转,输出又经过一次逆转

大数减法:

前提条件:
  • 大数A - 大数B:要求A > B
  • 检验方法:strcmp(a, b);行不通,因为无法解决a,b长度不等的情况
  • 如果A < B:B - A,之后结果添加负号
核心算法:
  1. cmp();函数:比较大数A 和 大数B
    鉴于都是正数,所以位数长的大,同位数的需要从高位比起
    大数减法图示
  2. 借位:C[i] = A[i] - B[i] - t
    • 算第i位上的数时:t是i-1位借走的,最多借1,计算第i位时已经知道i-1位的t值
    • 向i+1位借位时:若C[i] < 0则t=1,表示借1;若C[i] >= 0,则t=0,表示未借或借了0;
    • 由于是大数减小数,所以不会出现不够借位的情况,同时意味着这一位上必然是正数
//利用取模可以保证所得结果是正数,有两种写法
//已知A[i]-B[i]-t最小是-10:
C[i] = (A[i]-B[i]-t + 10) % 10;
//通法:负数%10后属于[-9,0],再+10属于[1,10]
C[i] = ((A[i]-B[i]-t)%10 + 10) % 10;
  1. 去除结果高位的 0 ,vector简单pop()
  • 🌟最容易忘记的点:cmp函数当两数相等返回0
  • 🌟最难的点:辅助变量t既用于计算当前位上数字 又用于记录借位
  • 🌟最化简的点:不论当前位是否借位,都等于(t+10)%10

写作步骤:

  • 三部分:

1. 比较函数cmp()

  • 均为正数:位长者大;位等从高位开始比;最终可能两数相等

2. 大数减法模拟

  • 第i位的数值C[i] = (A[i] - t -B[i] +10) % 10;
  • 第i位向第i+1位借位: A[i] - t -B[i]>=0则t=0,表示借了0;A[i] - t -B[i]<0则t=1,表示借了1。

3. 去除前导0

  • 前导0指的是高位的0,从vec.size()-1遍历到0

核心算法:

  1. 比较函数cmp()确保大数-小数
  2. 逆序输入,逆序输出
  3. 减法-借位变量t
  4. 去除前导0

代码注意:

四个易漏点:

1. 输入数据需要-‘0’
2. cmp()函数确保大数减小数
3. 编写cmp()函数时极易忘记两数相等情况
4. 前导0的去除

代码模板:

#include <iostream>
using namespace std;
bool cmp(vector<int> &A, vector<int> &B){
    if (A.size() != B.size())
        return A.size()>B.size();
    for(int i=A.size(); i>=0; i--){
        if (A[i] != B[i])
            return A[i] > B[i];
	}
    //注意点1:可能两个大数相等
    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] - t;
        if (i < B.size())
            t -= B[i];
        //注意点2:当前位先借10,再%10,囊括了借位和不接位的情况
        C.push((t + 10) %10);
        //注意点3:当前位减法结果小于0才借位
        if (t < 0) t = 1;
        else t = 0;
 }
    //注意点4:去除结尾的0
    while(C.size()>1 && C.back()==0)
        C.pop_back();
    return C;
    }
    int main(){
    string a, b;
    cin >> a >> b;
    vector<int> 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');
    }
    vector<int> C = sub(A,B);
    for(int i=C.size()-1; i>=0; i--){
        cout << C[i];
    }
	return 0;
}

代码误区:

1. 为什么不能用strcmp()进行大数比较?

  • strcmp从arr[0]开始比较,而vector<int>的vec[0]是个位,数字大小的比较看的是高位

本篇感想:

  • 写了40mins,还Ctrl CV了大数加法的部分内容,确实是花费时间长了
  • 有了大数加法的铺垫,大数减法的算法原理更加精炼了,希望大家能理解借位记录变量t
  • 从大数减法开始出现了前导0的去除,其实除了大数加法没有前导0,大数减法乘法除法都有,但是大数加法有最高位进位
  • 看完本篇博客,恭喜已登《练气境-初期》
    练气期初阶
    距离登仙境不远了,加油 登仙境初期
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

starnight531

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值