漫画算法-学习笔记(26)

漫画算法-小灰的算法之旅(26)

1. 如何实现大整数相加

Q:给出两个很大的整数,要求实现程序求出两个整数之和。注:整数可能超过long类型范围

方法(模拟竖式加加法)
思路

考虑到数字可能会超过基础数据类型,因此我们无法采用传统的计算方式来计算超大型数字。但我们学过「列竖时计算」,因此本题可以采用模拟【竖式加法】的计算过程,来实现问题求解。

实现步骤

第一步:创建两个整型数组,数组长度是较大整数的位数+1。然后把每一个整数倒叙存储到数组中,整数的个位存于数组下标为0的位置,最高位存于数组的尾部。之所以倒序存储,是因为这样更符合从左到右访问数组的习惯。

第二步:创建结果数组,结果数组的长度同样是较大整数的位数+1,+1的目的很明显,是给最高位仅为预留的。

第三步:遍历两个数组,从左到右按照对应下标把元素两两相加,就像「计算竖式」一样。在本示例中,最先想家的是数组A的第1个元素8和数组B的第1个元素9,结果是7,进位是1。把7填充到result数组的对应下标位置,进位的1填充到下一个位置。

第2组相加的是数组A的第2个元素1和数组B的第2个元素2,结果是3,再加上刚才的进位1,把4填充到result数组的对应下标位置。

第3组相加的是数组A的第3个元素3和数组B的第3个元素1,结果是4,把4填充到result数组的对应下标位置。

依次类推…一直把数组的所有元素都相加完毕为止。

第4步:把result数组的全部元素再次逆序,去掉首位的0,就是最终结果。

时间复杂度

该方法的创建数组、按位计算、结果逆序的时间复杂度均为O(n),因此整体的时间复杂度为O(n);空间复杂度,涉及到3个数组的创建,因此空间复杂度也为O(n)。

代码实现


/**
* 大整数求和
* param bigNumberA. 大整数A
* param bigNumberB  大整数B
*/
public static String bigNumberSum(String bigNumberA,String bigNumberB){
  // 1. 把两个大整数用数组逆序存储,数组长度等于较大整数位数+1
  int maxLength= bigNumberA.length()>bigNumberB.length() ? bigNumberA.length() : bigNumberB.length();
  int[] arrayA =new int[maxLength+1];
  int arrayB[] =new int[maxLength+1];
  for(int i=0;i<bigNumberA.length();i++){
    arrayA[i]=bigNumberA.charAt(bigNumberA.length()-1-i) -'0';
  }
  for(int i=0;i<bigNumberB.length();i++){
    arrayB[i]=bigNumberB.charAt(bigNumberB.length()-1-i) -'0';
  }
  //2. 构建result数组,数组长度等于较大整数位数+1
  int[] result=new int[maxLength+1];
  //3. 遍历数组,按位相加
  for(int i=0;i<result.length;i++){
    int temp=result[i];
    temp+=arrayA[i];
    temp+=arrayB[i];
    //判断是否进位
    if(temp>=10){
      temp=temp-10;
      result[i+1]=1;
    }
    result[i]=temp;
  }
  // 4. 把result数组再次逆序并转成String
  StringBuilder sb=new StringBuilder();
  //是否找到大整数的最高有效位
  boolean findFirst=false;
  for(int i=result.length-1;i>=0;i--){
    if(!findFirst){
      if(result[i]==0){
        continue;
      }
      findFirst=true;
    }
    sb.append(result[i]);
  }
  return sb.toString();
}

public static void main(String[] args){
  System.out.println(bigNumberSum("426709752318","95481253129"));
}
golang实现
func compareLength(strA, strB string) int {
	if len(strA) > len(strB) {
		return len(strA)
	}
	return len(strB)
}

func bigNumberSum(bigNumberA, bigNumberB string) string {

	//1. 把两个大整数用数组逆序存储,数组长度等于较大整数位数+1
	maxLength := compareLength(bigNumberA, bigNumberB)

	total := maxLength + 1
	arrayA := make([]int, total)
	arrayB := make([]int, total)
	for i := 0; i < len(bigNumberA); i++ {
		arrayA[i] = int(bigNumberA[len(bigNumberA)-1-i] - '0')
	}
	for i := 0; i < len(bigNumberB); i++ {
		arrayB[i] = int(bigNumberB[len(bigNumberB)-1-i] - '0')
	}
	//2. 构造接收result数组,数组长度等于较大整数位数+1
	result := make([]int, total)
	//3. 遍历数组,按位相加
	for i := 0; i < maxLength+1; i++ {
		temp := result[i]
		temp += arrayA[i]
		temp += arrayB[i]
		//判断是否进位
		if temp >= 10 {
			temp = temp - 10
			result[i+1] = 1
		}
		result[i] = temp
	}
	//4. 把result 数组再次逆序并转成string
	ans := ""
	findFirst := false
	for i := len(result) - 1; i >= 0; i-- {
		if !findFirst {
			if result[i] == 0 {
				continue
			}
			findFirst = true
		}
		ans += strconv.Itoa(result[i])
	}
	if ans == "" {
		return "0"
	}
	return ans
}

func main() {
	strA :="426709752318"
	strB :="95481253129"
	sum := bigNumberSum(strA, strB)
	log.Println(sum)
}
代码优化

依旧采用模拟「竖式计算」的方式求解,只不过我们可以不用新开辟内存空间来辅助计算,而是采用双指针的方式处理。具体步骤:

定义两个指针 ij分别指向bigNumberAbigNumberB的尾端,即最低位;并定义一个变量add维护当前是否有进位,然后从末尾到开头依次相加即可。你可能会想两个数字位数不同该如何处理,这里我们统一在指针当前下标处于负数的时候返回0,等价于对位较短的数字进行了补零操作,这样就可以除去两个数位不同情况的处理。

时间复杂度

改进后的时间复杂度为:O(max(len1,len2));即取决于「竖式加法」中加大数的位数

因为我们省去了辅助数组,并没有开辟新的空间,因此空间复杂度为O(1)。

golang实现

func bigNumberSumV2(bigNumberA, bigNumberB string) string {
	// 定义辅助进位标识
	add :=0
	// 接收计算结果
	ans :=""
	for i,j :=len(bigNumberA)-1, len(bigNumberB)-1;i>=0 || j>=0 || add!=0; i,j=i-1,j-1 {
		var x,y int
		if i>=0 {
			x=int(bigNumberA[i]-'0')
		}
		if j>=0 {
			y=int(bigNumberB[j]-'0')
		}
		// 获取每次求和值
		result :=x+y+add
		// 将结果对10求余可巧妙解决进位的问题
		ans=strconv.Itoa(result%10)+ans
		// 将结果相除可初始化进位
		add=result/10
	}
	return ans
}

func main() {
	strA :="426709752318"
	strB :="95481253129"
	v2 := bigNumberSumV2(strA, strB)
	log.Println(v2)

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值