算法题记录--大整数

当遇到算法题的输入数据超过常规数据类型(如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&&dividend.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&&dividend.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;
}
/*##-流提取和流插入运算符重载-##*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值