• 面试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;
    	if(nLen1 < nLen2 || (nLen1 == nLen2 && str1[0] < str2[0]))
    	{
    		ans[0] = '-';
    		if(Minus(str2, str1, ans + 1, nMaxLen - 1))
    			return ans;
    		else
    			return NULL;
    	}
    	int i,j,k;
    	int a,b,carry;
    	bool IsPos = true;
    	carry = 0;
    	//减去公共的部分,str1大于str2
    	for(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;
    		}
    		else
    			carry = 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;
    		}
    		else
    			carry = 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;
    		else
    			return NULL;
    	}
    	else if(str2[0] == '-')
    	{
    		ans[0] = '-';
    		if(Mutiply(str1, str2 + 1, ans + 1, nMaxLen))
    			return ans;
    		else
    			return 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;
    }



     

  • 相关阅读:
    ConcurrentHashMap get方法保持同步的解释
    HashMap resize导致死循环
    ConcurrentHashMap原理详解
    单列索引和联合索引区别
    优先队列
    大根堆排序
    小根堆排序
    基础哈夫曼树-最简单的
    二叉查找树
    二叉查找树
  • 原文地址:https://www.cnblogs.com/javawebsoa/p/3055191.html
Copyright © 2020-2023  润新知