二进制大数求和与十进制转换详解
问题描述
小U和小R对二进制数字充满了好奇。他们希望找到一种方法,可以将两个可能非常长的二进制字符串相加,并以十进制的形式呈现。由于二进制字符串可能非常长,常规的方法(如直接转换为整数类型进行计算)可能会导致数据溢出或性能问题。因此,需要设计一个时间复杂度不超过 O(n²) 的算法,来完成这个任务。
示例:
-
样例1:
- 输入:
binary1 = "101",binary2 = "110" - 输出:
'11'
- 输入:
-
样例2:
- 输入:
binary1 = "111111",binary2 = "10100" - 输出:
'83'
- 输入:
-
样例3:
- 输入:
binary1 = "111010101001001011",binary2 = "100010101001" - 输出:
'242420'
- 输入:
问题分析
挑战与限制
- 大数处理: 由于二进制字符串可能非常长,直接将其转换为整数类型(如
int、long)会导致数据溢出。 - 时间复杂度要求: 需要在时间复杂度 O(n²) 内完成计算,其中
n是二进制字符串的长度。
解决思路概述
- 二进制字符串相加: 首先,需要实现两个二进制字符串的加法,得到相加后的二进制结果。
- 二进制转十进制: 然后,将相加后的二进制字符串转换为十进制字符串。
- 大数运算: 由于无法使用内置的数据类型来处理超大的数字,需要使用字符串来模拟大数的加法和乘法。
详细解答
步骤一:实现二进制字符串的加法
思路:
- 从二进制字符串的末尾开始逐位相加,模拟手算的过程。
- 使用变量
carry来记录进位。 - 将结果存储在一个可变字符串(如
StringBuilder)中。
示意图:
binary1: 1 0 1
↑ ↑ ↑
i
binary2: 1 1 0
↑ ↑ ↑
j
carry: 0
sum = binary1[i] + binary2[j] + carry
实现:
public static String addBinaryStrings(String a, String b) {
StringBuilder result = new StringBuilder();
int i = a.length() - 1; // 指针 i 指向字符串 a 的末尾
int j = b.length() - 1; // 指针 j 指向字符串 b 的末尾
int carry = 0; // 进位
while (i >= 0 || j >= 0 || carry > 0) {
int sum = carry; // 当前位的和
if (i >= 0) {
sum += a.charAt(i) - '0';
i--;
}
if (j >= 0) {
sum += b.charAt(j) - '0';
j--;
}
result.append(sum % 2); // 当前位的结果
carry = sum / 2; // 更新进位
}
return result.reverse().toString(); // 反转字符串并返回
}
代码解释:
i和j指针: 从后往前遍历两个字符串。carry变量: 保存进位,可能为 0 或 1。sum变量: 当前位的总和,包括两个二进制位和进位。- 结果构建: 使用
StringBuilder来存储结果,最后反转得到正确的顺序。
步骤二:将二进制字符串转换为十进制字符串
思路:
- 模拟二进制转十进制的过程,从高位到低位遍历二进制字符串。
- 初始十进制结果为
'0',每次遍历到新的二进制位,将当前十进制结果乘以 2,再加上当前位的值。
示意图:
对于二进制字符串 "1101",转换过程如下:
-
初始化:
decimal = '0' -
遍历第一个字符
'1':decimal = multiplyByTwo(decimal)=>'0' * 2 = '0'decimal = addStrings(decimal, '1')=>'0' + '1' = '1'
-
遍历第二个字符
'1':decimal = multiplyByTwo(decimal)=>'1' * 2 = '2'decimal = addStrings(decimal, '1')=>'2' + '1' = '3'
-
遍历第三个字符
'0':decimal = multiplyByTwo(decimal)=>'3' * 2 = '6'- 当前位为
'0',不需要加 1
-
遍历第四个字符
'1':decimal = multiplyByTwo(decimal)=>'6' * 2 = '12'decimal = addStrings(decimal, '1')=>'12' + '1' = '13'
实现:
public static String binaryToDecimal(String binary) {
String decimal = "0"; // 初始化十进制结果为 "0"
for (int i = 0; i < binary.length(); i++) {
// 将十进制结果乘以 2
decimal = multiplyByTwo(decimal);
// 如果当前位为 '1',则加 1
if (binary.charAt(i) == '1') {
decimal = addStrings(decimal, "1");
}
}
return decimal;
}
步骤三:实现大数的字符串加法和乘法
字符串加法 addStrings
思路:
- 从低位到高位逐位相加,处理进位。
- 使用
StringBuilder存储结果,最后反转。
实现:
public static String addStrings(String num1, String num2) {
StringBuilder result = new StringBuilder();
int i = num1.length() - 1; // 指针 i 指向 num1 的末尾
int j = num2.length() - 1; // 指针 j 指向 num2 的末尾
int carry = 0; // 进位
while (i >= 0 || j >= 0 || carry > 0) {
int sum = carry; // 当前位的和
if (i >= 0) {
sum += num1.charAt(i) - '0';
i--;
}
if (j >= 0) {
sum += num2.charAt(j) - '0';
j--;
}
result.append(sum % 10); // 当前位的结果
carry = sum / 10; // 更新进位
}
return result.reverse().toString(); // 反转字符串并返回
}
字符串乘以 2 multiplyByTwo
思路:
- 从低位到高位逐位相乘,处理进位。
- 因为乘以 2,所以每一位的计算是当前数字乘以 2 加上进位。
实现:
public static String multiplyByTwo(String num) {
StringBuilder result = new StringBuilder();
int carry = 0; // 进位
for (int i = num.length() - 1; i >= 0; i--) {
int product = (num.charAt(i) - '0') * 2 + carry;
result.append(product % 10);
carry = product / 10;
}
if (carry > 0) {
result.append(carry);
}
return result.reverse().toString(); // 反转字符串并返回
}
完整代码
public class Main {
public static void main(String[] args) {
// 测试样例
String binary1 = "101";
String binary2 = "110";
System.out.println(solution(binary1, binary2)); // 输出:11
binary1 = "111111";
binary2 = "10100";
System.out.println(solution(binary1, binary2)); // 输出:83
binary1 = "111010101001001011";
binary2 = "100010101001";
System.out.println(solution(binary1, binary2)); // 输出:242420
binary1 = "111010101001011";
binary2 = "10010101001";
System.out.println(solution(binary1, binary2)); // 输出:31220
binary1 = "11";
binary2 = "1";
System.out.println(solution(binary1, binary2)); // 输出:4
}
public static String solution(String binary1, String binary2) {
// 第一步:将两个二进制字符串相加
String sumBinary = addBinaryStrings(binary1, binary2);
// 第二步:将二进制字符串转换为十进制字符串
String decimalResult = binaryToDecimal(sumBinary);
return decimalResult;
}
// 实现二进制字符串的加法
public static String addBinaryStrings(String a, String b) {
StringBuilder result = new StringBuilder();
int i = a.length() - 1; // 指针 i 指向字符串 a 的末尾
int j = b.length() - 1; // 指针 j 指向字符串 b 的末尾
int carry = 0; // 进位
while (i >= 0 || j >= 0 || carry > 0) {
int sum = carry; // 当前位的和
if (i >= 0) {
sum += a.charAt(i) - '0';
i--;
}
if (j >= 0) {
sum += b.charAt(j) - '0';
j--;
}
result.append(sum % 2); // 当前位的结果
carry = sum / 2; // 更新进位
}
return result.reverse().toString(); // 反转字符串并返回
}
// 将二进制字符串转换为十进制字符串
public static String binaryToDecimal(String binary) {
String decimal = "0"; // 初始化十进制结果为 "0"
for (int i = 0; i < binary.length(); i++) {
// 将十进制结果乘以 2
decimal = multiplyByTwo(decimal);
// 如果当前位为 '1',则加 1
if (binary.charAt(i) == '1') {
decimal = addStrings(decimal, "1");
}
}
return decimal;
}
// 字符串表示的大整数加法
public static String addStrings(String num1, String num2) {
StringBuilder result = new StringBuilder();
int i = num1.length() - 1; // 指针 i 指向 num1 的末尾
int j = num2.length() - 1; // 指针 j 指向 num2 的末尾
int carry = 0; // 进位
while (i >= 0 || j >= 0 || carry > 0) {
int sum = carry; // 当前位的和
if (i >= 0) {
sum += num1.charAt(i) - '0';
i--;
}
if (j >= 0) {
sum += num2.charAt(j) - '0';
j--;
}
result.append(sum % 10); // 当前位的结果
carry = sum / 10; // 更新进位
}
return result.reverse().toString(); // 反转字符串并返回
}
// 将字符串表示的大整数乘以 2
public static String multiplyByTwo(String num) {
StringBuilder result = new StringBuilder();
int carry = 0; // 进位
for (int i = num.length() - 1; i >= 0; i--) {
int product = (num.charAt(i) - '0') * 2 + carry;
result.append(product % 10);
carry = product / 10;
}
if (carry > 0) {
result.append(carry);
}
return result.reverse().toString(); // 反转字符串并返回
}
}
代码详解
主函数 main
- 测试了给定的样例,调用
solution方法并输出结果。
方法 solution
-
参数:
binary1:第一个二进制字符串。binary2:第二个二进制字符串。
-
返回值:
- 相加后的二进制字符串转换为十进制字符串。
-
逻辑:
- 调用
addBinaryStrings方法,计算两个二进制字符串的和,得到sumBinary。 - 调用
binaryToDecimal方法,将sumBinary转换为十进制字符串decimalResult。 - 返回
decimalResult。
- 调用
方法 addBinaryStrings
-
功能:
- 实现两个二进制字符串的加法。
-
逻辑:
- 使用指针
i和j从后往前遍历字符串a和b。 - 在循环中,计算当前位的和
sum,包括进位carry。 - 将
sum % 2作为当前位的结果,carry = sum / 2作为新的进位。 - 最后,反转结果字符串并返回。
- 使用指针
方法 binaryToDecimal
-
功能:
- 将二进制字符串转换为十进制字符串。
-
逻辑:
-
初始化十进制结果
decimal = "0"。 -
遍历二进制字符串的每一位:
- 将当前十进制结果乘以 2,调用
multiplyByTwo方法。 - 如果当前位为
'1',则调用addStrings方法,加上'1'。
- 将当前十进制结果乘以 2,调用
-
方法 addStrings
-
功能:
- 实现两个十进制字符串的加法。
-
逻辑:
- 使用指针
i和j从后往前遍历字符串num1和num2。 - 在循环中,计算当前位的和
sum,包括进位carry。 - 将
sum % 10作为当前位的结果,carry = sum / 10作为新的进位。 - 最后,反转结果字符串并返回。
- 使用指针
方法 multiplyByTwo
-
功能:
- 将十进制字符串表示的数字乘以 2。
-
逻辑:
- 从后往前遍历字符串
num。 - 对每一位,计算
product = 当前位数字 * 2 + carry。 - 将
product % 10作为当前位的结果,carry = product / 10作为新的进位。 - 最后,反转结果字符串并返回。
- 从后往前遍历字符串
复杂度分析
-
时间复杂度:
addBinaryStrings方法: O(max(m, n)),其中m和n是两个二进制字符串的长度。binaryToDecimal方法: 对于长度为k的二进制字符串,内部循环(乘法和加法)每次操作的时间复杂度为 O(L),其中L是当前十进制字符串的长度,最坏情况下L = O(k)。因此,总的时间复杂度为 O(k²)。- 总体时间复杂度: O(n²),满足题目的要求。
-
空间复杂度:
- 主要是存储中间结果的字符串,空间复杂度为 O(n)。
注意: 该算法适用于任意长度的二进制字符串,但由于时间复杂度为 O(n²),对于非常长的字符串,计算时间可能会较长。在实际应用中,如果需要处理更长的字符串,可以考虑进一步优化算法,或者使用更高效的大数运算库。