「这是我参与11月更文挑战的第2天,活动详情查看:2021最后一次更文挑战」
题意描述
给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。
示例 1:
输入: num1 = "2", num2 = "3" 输出: "6"
示例 2:
输入: num1 = "123", num2 = "456" 输出: "56088"
说明:
- num1 和 num2 的长度小于110。
- num1 和 num2 只包含数字 0-9。
- num1 和 num2 均不以零开头,除非是数字 0 本身。
- 不能使用任何标准库的大数类型(比如 BigInteger)或直接将输入转换为整数来处理。
解题思路
整体的思想是用的竖式乘法,比较常规,并且再做了两处优化
- 乘积缓存,做了相应的记忆化,不需要进行重复的计算。
- 先将乘法、加法全部执行完之后再统一进位
class Solution {
public String multiply(String num1, String num2) {
//容纳乘积的数组,乘积长度最长为二者长度之和
//存放时从index=0开始放个位数
int[] ans = new int[num1.length() + num2.length()];
//遍历
for(int i = num1.length()-1;i>=0;i--){
int n1 = Integer.parseInt(num1.charAt(i)+"");
int index1 = num1.length()-1-i;
for(int j=num2.length()-1;j>=0;j--){
int n2 = Integer.parseInt(num2.charAt(j)+"");
int index2 = num2.length()-1-j;
int index = index1 + index2; //乘积的个位 对应到数组的下标
int sum = n1 * n2; //两个一位数相乘,结果最多为两位数
addToAns(ans,index,sum);
}
}
StringBuilder builder = new StringBuilder();
boolean ignoreZero = true; //忽略最高位的0
for(int i = ans.length-1;i>=0;i--){ //从数组转换成字符串
int num = ans[i];
if(num==0){
if(ignoreZero){
continue;
}else{
builder.append(num);
}
}else{
ignoreZero = false; //从高到低位一旦有一个不是0,后面就不需要忽略0了
builder.append(num);
}
}
return builder.length()==0? "0" :builder.toString(); //防止 0+0=0的情况
}
public void addToAns(int[] ans,int index,int sum){ //累加到数组里
int sum1 = sum % 10; //个位
int sum2 = sum / 10; //十位
//累加到数组中
int sum3 = ans[index] + sum1; //个位累加结果
ans[index] = sum3 % 10; //得到个位值
index++;
int sum4 = sum2 + sum3/10 + ans[index]; //十位累加结果
ans[index] = sum4 % 10; //得到十位值
//进位处理
index++;
while(index<ans.length){
if(sum4 >= 10){
//需要进位
sum4 = ans[index]++;
}else{
//不需要进位了,直接结束
break;
}
index++;
}
}
}
复盘分析
复杂度分析,设两个整数的位数分别为 n 和 m
- 时间复杂度 O(n + m)
- 空间复杂度 O(n + m)
乘法竖式原理
其实乘法竖式也类似 => 列竖式
-
选定排在下面的一个数字并根据这个位置来决定第三步中第一个乘积的落点,每往前选取1个,乘积落点往前移一格
-
排在上面的数字倒序选一个,每往前一个,乘积落点往前移一格
-
每一步把选定的两位乘起来,放到对应的位上。每超过10往前一位加1
-
收集结果,选定排在下面的下一个数字,重复步骤2-步骤4直到下面的数字没得选了
-
将4中收集的结果做加法的步骤进行循环