题目描述
给定两个以字符串形式表示的非负整数 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)或直接将输入转换为整数来处理。
分析
此题是一道经典的题目,曾经被华为、腾讯等众多公司作为笔试、面试题,该题有一定的难度,如果没有准备过则需要花一定时间去写的,而且该题有一定技巧的,如果没设计好结构直接解析字符串硬乘,然后进位相加则困难很大。大数相乘法主要有模拟手工计算的普通大数相乘法、分治算法和FFT算法等三种方法,面试中只有会用普通大数相乘法解决就足够了,下面我将分析下普通大数相乘法。
解题思路
普通相乘是按位相乘,然后将相应的位相加,最后考虑进位。
例如:
A = 17 = 1 * 10 + 7 = (7,1) 转换成十进制的幂表示法,幂次是从低位到高位
B = 25 = 2 * 10 + 5 = (5,2)
C = A * B = (7 * 5, 1 * 5 + 2 * 7, 1 * 2) = (35, 19 ,2)
进位处理过程: (35, 19, 2) = (5, 22, 2) = (5, 2, 4) = 425
处理的整体步骤为:
- 将字符串按位相乘,并加结果放到
result[i+j]
r
e
s
u
l
t
[
i
+
j
]
中(相乘相加的过程)
- 将
result[i+j]
r
e
s
u
l
t
[
i
+
j
]
结果按低位到高位处理进位
- 将处理进位后的结果以字符串形式表示出来
public String multiply(String num1, String num2) {
if (num1.equals("0") || num2.equals("0"))
return "0";
StringBuilder sb = new StringBuilder();
int a = num1.length();
int b = num2.length();
int[] nums = new int[a + b - 2 + 1];
for (int i = 0 ; i < a ; i ++) {
for (int j = 0 ; j < b ; j ++) {
// 按位相乘并保存到 i + j 处
nums[i + j] += Integer.valueOf(num1.charAt(i) - '0') * Integer.valueOf(num2.charAt(j) - '0');
}
}
// 进位处理,将结果转换成字符串表示
int carry = 0;
for (int i = nums.length - 1 ; i >= 0 ; i --) {
int tmp = nums[i] + carry;
sb.append(tmp % 10);
carry = tmp / 10;
}
// 处理最高位的进位
while (carry > 0) {
sb.append(carry % 10);
carry = carry / 10;
}
// 反转字符串
return sb.reverse().toString();
}
- 时间复杂度:该算法使用了两层循环,循环次数分布为 m(数1的长度)和 n(数2的长度),因此时间复杂度为O(m * n)。
- 空间复杂度:该算法占用的空间复杂度为 O(m + n)。