面試100題系列之13大數的四則運算

關於大數的四則運算,我想網上有很多方法,感覺通用性不強,然後呢,功能不強大,效率不高~~問題多多啊~然後呢,大數這個東西又經常碰到,所以呢,我一咬牙,寫了一個比較完整的,花了我一下午的時間啊,不過除法還沒有寫,有時間再寫吧。最近事情太多了,寫了之後再補上。
OK,說一下我的思路。首先這裏用字符串存數據,這個地球人都知道。第0位是高位。當我們算加減的時候需要從低位開始算起。所以我一開始的思路是將字符串反轉一下,然後開始計算,這樣也不會擔心最後有進位或者借位了,計算好之後再反轉回來。OK,這樣我爲了算一個加法,反轉四次啊!!!代價有點大,不能接受。
怎麼樣能既方便運算又不用反轉那麼多呢?要是隻反轉一次就好了。如果把結果的數組指針傳進去,算的時候分別從兩個字符串尾部開始遍歷,將結果從下標0開始一次存入結果數組。最後把結果數組反轉一下就可以了。好了,加減搞定了。
對於乘法,str1[i]與str2[j]乘的結果放在結果數組的i+j位,這個結論怎麼來的呢?其實也就是我們寫的豎式啦,寫寫看~這裏需要一個臨時的int數組arr來存放乘的結果,arr[i+j]存放所有滿足條件的i和j的元素之和。然後將arr裏面的數據拆分到ans數組裏面就可以了。
最後說明一下,每個函數加入一個nMaxLen的參數是保證計算的結果不會溢出,也就是不會超過ans數組的最大容量,這裏我採用的辦法是返回一個NULL,但是這樣可能與一些函數(比如說printf)嵌套使用的時候就不是很方便。這裏其實可以將返回NULL的操作替換成往結果數組裏面打印相應的字符串,來表明錯誤。
路過的大爺們,轉載請註明出處:http://blog.csdn.net/kay_zhyu/article/details/8877032哦~~小女子萬分感激~~~
核心代碼如下,這裏支持負數運算哦,歡迎大家找Bug:

//兩個字符串相加
//str1和str2是加數,ans是結果數組,nMaxLen是結果數組的最大空間
char* Add(char* const str1, char* const str2, char *ans, int nMaxLen)
{
	//一些參數檢驗
	if(!str1 || !str2 || !ans || nMaxLen < 1)
		return NULL;
	//保證是兩個整數相加
	if(str1[0] == '-' && str2[0] == '-')//負加負
	{
		ans[0] = '-';
		if(Add(str1 + 1, str2 + 1, ans + 1, nMaxLen - 1))
			return ans;
		else
			return NULL;
	}
	else if(str1[0] == '-')//一正一負
	{
		return Minus(str2, str1 + 1, ans, nMaxLen);
	}
	else if(str2[0] == '-')
	{
		return Minus(str1, str2 + 1, ans, nMaxLen);
	}
	int nLen1 = strlen(str1);
	if(nMaxLen < nLen1)
		return NULL;
	int nLen2 = strlen(str2);
	if(nMaxLen < nLen2)
		return NULL;
	int i,j,k;
	int a,b,carry;
	carry = 0;
	//加公共部分
	for(i = nLen1 - 1, j = nLen2 - 1, k = 0;
		(i >= 0 && j >= 0 && k < nMaxLen); --i,--j,++k)
	{
		a = str1[i] - '0';
		b = str2[j] - '0';
		ans[k] = (a + b + carry) % 10 + '0';
		carry = (a + b + carry) / 10;
	}
	//加str1的多餘部分
	for( ; (i >= 0 && k < nMaxLen); --i, ++k)
	{
		a = str1[i] - '0';
		ans[k] = (a + carry) % 10 + '0';
		carry = (a + carry) / 10;
	}
	//加str2的多餘部分
	for( ; (j >= 0 && k < nMaxLen); --j, ++k)
	{
		b = str1[j] - '0';
		ans[k] = (b + carry) % 10 + '0';
		carry = (b + carry) / 10;
	}
	if(carry > 0 && k < nMaxLen)
		ans[k++] = carry + '0';
	//結果的越界檢查
	if(k >= nMaxLen)
		return NULL;
	//加上結束標記
	ans[k] = '\0';
	Invert(ans, k);
	return ans;
}
//兩個大數相減
//str1-str2,ans表示結果,nMaxLen表示ans的最大長度
char* Minus(char* const str1, char * const str2, char *ans, int nMaxLen)
{
	//一些參數檢驗
	if(!str1 || !str2 || !ans || nMaxLen < 1)
		return NULL;
	//保證是正數減正數
	if(str1[0] != '-' && str2[0] == '-')//正減負
		return Add(str1, str2 + 1, ans, nMaxLen);
	else if(str1[0] == '-' && str2[0] == '-')//負減負
		return Minus(str2 + 1, str1 + 1, ans, nMaxLen);
	else if(str1[0] == '-' && str2[0] != '-')//負減正
	{
		ans[0] = '-';
		if(Add(str1 + 1, str2, ans + 1, nMaxLen - 1))
			return ans;
		else
			return NULL;
	}
	//保證str1大於str2
	int nLen1 = strlen(str1);
	if(nMaxLen < nLen1)
		return NULL;
	int nLen2 = strlen(str2);
	if(nMaxLen < nLen2)
		return NULL;
//其實這裏直接是str1[0]<str2[0]是有bug的,應該是strcmp(str1,str2),確認str1<str2
if(nLen1 < nLen2 || (nLen1 == nLen2 && str1[0] < str2[0])){ans[0] = '-';if(Minus(str2, str1, ans + 1, nMaxLen - 1))return ans;elsereturn NULL;}int i,j,k;int a,b,carry;bool IsPos = true;carry = 0;//減去公共的部分,str1大於str2for(i = nLen1 - 1, j = nLen2 - 1, k = 0;(i >= 0 && j >= 0 && k < nMaxLen); --i,--j,++k){a = str1[i] - '0' - carry;b = str2[j] - '0';if(a < b){a += 10;carry = 1;}elsecarry = 0;ans[k] = a - b + '0';}//得到str1剩餘的部分for(; (i >= 0 && k < nMaxLen); --i,++k){a = str1[i] - '0' - carry;if(a < carry){a += 10;carry = 1;}elsecarry = 0;ans[k] = a + '0';}if(carry > 0 && k < nMaxLen)ans[k++] = '-';if(k >= nMaxLen)return NULL;ans[k] = '\0';Invert(ans, k);return ans;}//大數乘法//str1*str2,ans是結果,nMaxLen是ans的最大容量char *Mutiply(char* const str1, char* const str2, char *ans, int nMaxLen){if(!str1 || !str2 || !ans || nMaxLen < 1)return NULL;//保證是兩個正數相乘if(str1[0] == '-' && str2[0] == '-')return Mutiply(str1 + 1, str2 + 1, ans, nMaxLen);else if(str1[0] == '-'){ans[0] = '-';if(Mutiply(str1 + 1, str2, ans + 1, nMaxLen))return ans;elsereturn NULL;}else if(str2[0] == '-'){ans[0] = '-';if(Mutiply(str1, str2 + 1, ans + 1, nMaxLen))return ans;elsereturn NULL;}int nLen1 = strlen(str1);int nLen2 = strlen(str2);if(nMaxLen < nLen1 + nLen2 - 1)//內存不夠,返回return NULL;int *arr = (int *)malloc((nLen1 + nLen2 + 1)*sizeof(int));if(!arr)//內存申請失敗,返回return NULL;memset(arr, 0, (nLen1 + nLen2 + 1)*sizeof(int));int i,j;for(i = 0; i < nLen1; ++i){for(j = 0; j < nLen2; ++j){arr[i+j] += (str1[i] - '0') * (str2[j] - '0');}}int k,carry;carry = 0;//乘積的結果轉移到ans中for(k = 0, i = nLen1 + nLen2 - 2; i >= 0 && k < nMaxLen; --i,++k){ans[k] = (arr[i] + carry) % 10 + '0';carry = (arr[i] + carry) / 10;}free(arr);//釋放輔助內存arr = NULL;while(carry > 0 && k < nMaxLen){ans[k++] = carry % 10 + '0';carry /= 10;}//檢查是否溢出if(k > nMaxLen)return NULL;ans[k] = '\0';Invert(ans, k);return ans;}

照舊,給出輔助函數和變量定義,以及main函數的調用,不需要者可以pass~~~~

#include<stdio.h>
#include<string.h>
#include<malloc.h>
char* Add(char* const str1, char* const str2, char *ans, int nMaxLen);
char* Minus(char* const str1, char * const str2, char *ans, int nMaxLen);
//反轉字符串
void Invert(char *str, int nLen)
{
	if(!str || nLen < 1)
		return;
	char temp;
	for(int i = 0; i < nLen>>1; ++i)
	{
		temp = str[i];
		str[i] = str[nLen - 1 - i];
		str[nLen - 1 - i] = temp;
	}
}
int main()
{
	const int N = 50;
	char str1[N];
	char str2[N];
	char ans[N];
	char *p;
	while(scanf("%s %s", str1, str2) != EOF)
	{
		p = Add(str1, str2, ans, N);//加
		if(p)
			puts(p);
		p = Minus(str1, str2, ans, N);//減
		if(p)
			puts(p);
		p = Mutiply(str1, str2, ans, N);//乘
		if(p)
			puts(p);	
	}
	return 0;
}



 

發佈了171 篇原創文章 · 獲贊 173 · 訪問量 17萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章