当遇到算法题的输入数据超过常规数据类型(如int,long等)的存储范围时,又或者算法计算的中间过程或最终结果超过存储范围时,便需要考虑使用数组来存放该输入或结果。
然而,并非所有该类型问题均需要使用大整数,有的题目往往可以巧妙地回避大整数,因此需要对算法题进行一定分析,而非无脑地套用大整数。而且,当需要用到大整数的时候,也并非全部的操作均需要用上,需要根据算法题的要求选择适当的操作,同时也可以自己新增新的函数以方便实现算法要求。
本文的代码均摘自王道《计算机考研–机试指南(第2版)》,本人仅添加适当的注释,以供自身学习、复习所用,若有侵权,可联系删除,谢谢!
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
using namespace std;
const int maxn=10001; //数据的最大位数
struct bigint{
int digit[maxn];
int length;
bigint();
bigint(int x);
bigint(const string & str);
bigint(const bigint & x); //4个构造函数,包括默认构造函数,以及另外三个分别对应int,string以及bigint类型
bigint operator=(int x);
bigint operator=(const string & str);
bigint operator=(const bigint & x); //同理,三个赋值运算符重载函数,分别对应int,string以及bigint类型
bool operator<=(const bigint & x); //重载两个逻辑运算符,由于减法需要保证是大数减小数,因此需要重载<=运算符
bool operator==(const bigint & x);
bigint operator+(const bigint & x); //重载 加减乘除以及求余 运算符
bigint operator-(const bigint & x); //减法只能实现大数减小数
bigint operator*(const bigint & x);
bigint operator/(const bigint & x);
bigint operator%(const bigint & x);
friend ostream & operator<<(ostream & os, const bigint & x); //友元,重载流插入和流提取运算符
friend istream & operator>>(istream & is, bigint & x);
};
/*##+四个构造函数+##*/
bigint::bigint(){
memset(digit,0,sizeof(digit)); //初始化数组为全0
length=0;
}
bigint::bigint(int x){
memset(digit,0,sizeof(digit));
length=0;
if(x==0){ //“0”属于特殊情况,数值为0,长度为1
digit[length++]=0;
}
while(x){ //非0情况,则将每位填入。注意此处按正常观念,即数组的低位存数的低位,如“123”,3为最低位,存放在digit[0]
digit[length++]=x%10;
x/=10;
}
}
bigint::bigint(const string & str){
memset(digit,0,sizeof(digit));
length=str.size();
for(int i=0;i<length;i++){
digit[i]=str[length-1-i]-'0'; //string类型的大整数,存放的位置与正常相反,即数组低位存放数的高位,如“123”,1存放在str[0],因此需要将其倒过来
}
}
bigint::bigint(const bigint & x){
memset(digit,0,sizeof(digit));
length=x.length;
for(int i=0;i<length;i++){
digit[i]=x.digit[i];
}
}
/*##-四个构造函数-##*/
/*##+三个赋值运算符重载函数+##*/
bigint bigint::operator=(int x){
memset(digit,0,sizeof(digit)); //所有赋值运算符重载函数均需要将数重置为全0,以确保以后运算中结果正确
length=0;
if(x==0){
digit[length++]=0;
}
while(x){
digit[length++]=x%10;
x/=10;
}
}
bigint bigint::operator=(const string & str){
memset(digit,0,sizeof(digit));
length=str.size();
for(int i=0;i<length;i++){
digit[i]=str[length-1-i]-'0';
}
}
bigint bigint::operator=(const bigint & x){
memset(digit,0,sizeof(digit));
length=x.length;
for(int i=0;i<length;i++){
digit[i]=x.digit[x];
}
}
/*##-三个赋值运算符重载函数-##*/
/*##+两个逻辑运算符重载+##*/
bool bigint::operator<=(const bigint & x){
if(length<x.length){
return true; // 位数比x少,则小于
}else if(length>x.length){
return false; //位数比x多,则大于
}else{
for(int i=length-1;i>=0;i--){ //从高位开始,往低位方向,逐位遍历
if(digit[i]==x.digit[i]){ //相等则下一位
continue;
}else{
return digit[i]<x.digit[i]; //若存在某一位小于或大于,则相应返回
}
}
}
return true; //遍历完,则两个数相等
}
bool bigint::operator==(const bigint & x){
if(length!=x.length){
return false;
}else{
for(int i=0;i<length;i++){
if(digit[i]==x.digit[i]){
continue;
}else{
return false;
}
}
}
return true;
}
/*##-两个逻辑运算符重载-##*/
/*##+加减乘除以及求余运算符重载函数+##*/
bigint bigint::operator+(const bigint & x){
bigint result;
int carry=0, temp=0;
for(int i=0;i<length||i<x.length;i++){ //因为每个bigint均曾被初始化过所有位为0,因此此处尽管两个bigint位数不同,也能直接相加
temp=digit[i]+x.digit[i]+carry;
result.digit[result.length++]=temp%10;
carry=temp/10;
}
if(carry>0){
result.digit[result.length++]=carry;
}
return result;
}
bigint bigint::operator-(const bigint & x){ //减法只能大数减小数
bigint result;
int carry=0, temp=0;
for(int i=0;i<length;i++){
temp=digit[i]-x.digit[i]-carry;
if(temp<0){
temp+=10; //不够减的时候,借位
carry=1;
}else{
carry=0;
}
result.digit[result.length++]=temp;
}
while(result.digit[result.length-1]==0&&result.length>1){ //原书“[]”里写的是result.length,明显是不对的,result.length-1才是数的最高位的数组下标
result.length--; //把数的高位多余0清除掉
}
return result;
}
bigint bigint::operator*(const bigint & x){
bigint result;
result.length=length+x.length;//预设值结果的长度
for(int i=0;i<length;i++){
for(int j=0;j<x.length;j++){
result.digit[i+j] += digit[i]*x.digit[j]; //逐位乘,并累加
}
}
for(int i=0;i<result.length;i++){
result.digit[i+1]=result.digit[i]/10; //将大于10的位向后一位进位
result.digit[i]%=10;
}
while(resul.digit[result.length-1]==0&&result.length>1){
result.length--; //同样,清除高位多余的0
}
return result;
}
bigint bigint::operator/(const bigint & x){
bigint result;
result.length=length;
bigint dividend(0), divisor(x); //新建 被除数 和 除数 两个大整数变量,用于除法运算
for(int i=length-1;i>=0;i--){
if(!(dividend.length==1&÷nd.digit[0]==0)){ //当被除数不是0的时候,由于上一轮已经不断做了减法,此时的被除数必然比除数小,因此需要补位
for(int j=dividend.length-1;j>=0;j--){
dividend.digit[j+1]=dividend.digit[j]; //将数往后移,腾出低位,进行补位
}
dividend.length++;
}
dividend.digit[0]=digit[i]; //补位
while(divisor<=dividend){ //基本思路是用减法模拟除法,因此循环条件是 除数 要≤ 被除数
dividend=dividend-divisor;
result.digit[i]++; //成功减一次,则商加1
}
}
while(resul.digit[result.length-1]==0&&result.length>1){
result.length--; //同样,清除高位多余的0
}
return result;
}
bigint bigint::operator%(const bingint & x){
bigint dividend(0), divisor(x); //同样新建 被除数 和 除数 两个变量
for(int i=length-1;i>=0;i--){ //求余操作与除法操作基本相同,唯一不同的是,最终的被除数就是余数,因为它不够减,又不能再从低位进行补位了
if(!(dividend.length==1&÷nd.digit[0]==0)){
for(int j=dividend.length-1;j>=0;j--){
dividend.digit[j+1]=digit[j];
}
dividend.length++;
}
dividend.digit[0]=digit[i];
while(divisor<=dividend){
dividend=dividend-divisor;
}
}
return dividend;
}
/*##-加减乘除以及求余运算符重载-##*/
/*##+流提取和流插入运算符重载+##*/
ostream & operator<<(ostream & os, const bigint & x){
for(int i=x.length-1;i>=0;i--){ //大整数的存放规则是,数的高位存放在数组的高位
os<<x.digit[i]
}
return os;
};
istream & operator>>(istream & is, bigint & x){
string temp; //利用string类型来暂存,然后将其赋值给大整数
is>>temp;
x=temp;
return is;
}
/*##-流提取和流插入运算符重载-##*/