LeetCode之67.二进制求和
Posted 二木成林
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode之67.二进制求和相关的知识,希望对你有一定的参考价值。
题目
题目描述
给你两个二进制字符串,返回它们的和(用二进制表示)。
输入为 非空 字符串且只包含数字 1 和 0。
示例 1
输入: a = “11”, b = “1”
输出: “100”
示例 2
输入: a = “1010”, b = “1011”
输出: “10101”
提示
- 每个字符串仅由字符 ‘0’ 或 ‘1’ 组成。
- 1 <= a.length, b.length <= 10^4
- 字符串如果不是 “0” ,就都不含前导零。
来源
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/add-binary
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题解
解法1
该解法是最暴力的解法,但也是不可能被通过的解法。思路是这样的:利用API将两个二进制字符串(例如:“11"和"1”)转换成十进制整数(例如:“11”->“3"和"1”->“1”),然后以十进制的方式相加(例如:3+1=4),然后将结果再转换成二进制字符串(“4”->“100”)。但这样存在一个问题,就是编程语言所支持的整数数据类型长度是有限的,比如int类型最大正数只能表示2^31-1,而二进制字符串的长度几乎可以无限长。
代码如下:
public class Solution {
/**
* <p>思路:栈</p>
* <p>步骤:</p>
* <ul>
* <li>第一步,将两个字符串分别填入两个栈中</li>
* <li>第二步,同时循环遍历两个栈</li>
* <li>第三步,处理两个栈中的剩余元素,其实只有一个栈有剩余元素</li>
* <li>第四步,收尾工作,注意需要判断最后是否还存在进位,如果存在则需要添加一个'1'。比如"1010"和"1011"</li>
* </ul>
* <p>结果:成功</p>
* <ul>
* <li>执行用时:4 ms, 在所有 Java 提交中击败了17.51% 的用户</li>
* <li>内存消耗:38.5 MB, 在所有 Java 提交中击败了27.71% 的用户</li>
* <li>通过测试用例:294 / 294</li>
* </ul>
*
* @param a 第一个二进制字符串
* @param b 第二个二进制字符串
* @return 两个二进制字符串相加的和
*/
public String addBinary(String a, String b) {
// 第一步,将两个字符串分别填入两个栈中
Stack aStack = getStack(a);
Stack bStack = getStack(b);
// 第二步,同时循环遍历两个栈
StringBuilder result = new StringBuilder();// 保存相加结果的字符串
boolean isCarry = false;// 是否进位的标志,true表示两位字符相加存在进位('1'+'1'),false表示不存在进位('0'+'1'或'0'+'0')
while (!aStack.isEmpty() && !bStack.isEmpty()) {
char aBit = (char) aStack.pop();// 获取栈顶元素,并且将栈顶元素出栈
char bBit = (char) bStack.pop();
if (aBit == bBit) {// '0'+'0'=96 或 '1'+'1'=98
result.insert(0, isCarry ? '1' : '0');// 如果上两位相加有进位,则0+0+1=01或1+1+1=11;如果上两位相加没有进位,则0+0+0=00或1+1+0=10
isCarry = aBit == '1';// 至于当前两位相加的结果是否有进位,0+0=0一定没有进位,1+1=10一定有进位
} else {// '0'+'1'=97 或 '1'+'0'=91
result.insert(0, isCarry ? '0' : '1');// 如果上两位相加有进位,则0+1+1=10;如果上两位相加没有进位,则0+1+0=01
// 至于当前两位相加的结果是否有进位,取决于上两位相加是否有进位,所以isCarry不变
}
}
// 第三步,处理两个栈中的剩余元素,其实只有一个栈有剩余元素
while (!aStack.isEmpty()) {
char c = (char) aStack.pop();
if (!isCarry) {// 如果没有进位,则插入原值
result.insert(0, c);
isCarry = false;
} else {// 如果有进位,则如果c为1插入0,如果c为0则插入1
result.insert(0, '1' - c);
isCarry = '1' - c == 0;
}
}
while (!bStack.isEmpty()) {
char c = (char) bStack.pop();
if (!isCarry) {// 如果没有进位,则插入原值
result.insert(0, c);
isCarry = false;
} else {// 如果有进位,则如果c为1插入0,如果c为0则插入1
result.insert(0, '1' - c);
isCarry = '1' - c == 0;
}
}
// 第四步,收尾工作,注意需要判断最后是否还存在进位,如果存在则需要添加一个'1'。比如"1010"和"1011"
if (isCarry) {
result.insert(0, '1');
}
return result.toString();
}
/**
* <p>将字符串中的所有字符填入栈中。</p>
*
* @param str 字符串
* @return 返回填满字符的栈
*/
private Stack getStack(String str) {
char[] chars = str.toCharArray();
Stack stack = new Stack();
for (char aChar : chars) {
stack.push(aChar);
}
return stack;
}
}
解法2
二进制的相加和十进制的相加其实是一样的,都是从最后一位开始往前加,同时兼顾进位的问题,如下图:
两位二进制相加存在三种情况:
- 0+0=00
- 0+1=01
- 1+1=10
而在当前两位二进制相加后,为了得到最终的结果,还必须考虑上两位相加产生的进位(如果产生进位则为1,没有产生则为0),所以有如下六种情况:
- 00+1=01
- 00+0=00
- 01+1=10
- 01+0=01
- 10+1=11
- 10+0=10
知道了思路,就明白了代码如何写,但仍然有几点需要注意:第一,如果两个二进制字符串的长度不一样,那么必须对长度短的字符串进行高位补零;第二,在计算完当前两位二进制最终结果后需要对进位标志重新设定;第三,当遍历完字符串后,仍然需要最后判断一次进位标志,因为字符串的最高位相加可能产生进位。代码如下:
public class Solution {
/**
* <p>思路:单层for循环</p>
* <p>步骤:</p>
* <ul>
* <li>第一步,判断两个二进制字符串长度是否相等,如果不等则在短的二进制字符串的高位补0</li>
* <li>第二步,创建局部变量</li>
* <li>第三步,倒序遍历两个字符数组,进行相加操作</li>
* <li>第四步,收尾工作,注意需要判断最后是否还存在进位,如果存在则需要添加一个'1'。比如"1010"和"1011"</li>
* </ul>
* <p>结果:成功</p>
* <ul>
* <li>执行用时:2 ms, 在所有 Java 提交中击败了94.75% 的用户</li>
* <li>内存消耗:38.5 MB, 在所有 Java 提交中击败了30.24% 的用户</li>
* <li>通过测试用例:294 / 294</li>
* </ul>
*
* @param a 第一个二进制字符串
* @param b 第二个二进制字符串
* @return 两个二进制字符串相加的和
*/
public String addBinary(String a, String b) {
// 第一步,判断两个二进制字符串长度是否相等,如果不等则在短的二进制字符串的高位补0
// 例如:a="11",b="1",那么处理后是a="11",b="01"
if (a.length() > b.length()) {
b = addZero(b, a.length() - b.length());
} else if (a.length() < b.length()) {
a = addZero(a, b.length() - a.length());
}
// 第二步,创建局部变量
char[] aChars = a.toCharArray();// 将a字符串转换成字符数组
char[] bChars = b.toCharArray();// 将b字符串转换成字符数组
boolean isCarry = false;// 是否进位的标志,true表示两位字符相加存在进位('1'+'1'),false表示不存在进位('0'+'1'或'0'+'0')
StringBuilder result = new StringBuilder();// 保存相加结果的字符串
// 第三步,倒序遍历两个字符数组,进行相加操作
for (int i = a.length() - 1; i >= 0; i--) {
char aBit = aChars[i];
char bBit = bChars[i];
if (aBit + bBit == 96) {// '0'+'0'=96
// 在插入之前,需要先判断上两位相加是否有进位,因为0+0=0,如果前面有进位,则需要插入'1',如果前面没有进位则插入'0'
result.insert(0, isCarry ? '1' : '0');
// 设置当前相加(是在已经判断上两位相加的基础上的处理)是否会产生进位,0+0=0不会产生进位
isCarry = false;
} else if (aBit + bBit == 97) {// '0'+'1'=97 或 '1'+'0'=91
// 在插入之前,需要先判断上两位相加是否有进位,因为0+1=1,如有进位则会1+1=10,也会产生进位,则需要插入'0';如果没有进位则仍然插入两个相加的结果'1'
result.insert(0, isCarry ? '0' : '1');
// 如果上两位没有进位,则0+1=1不会进位,则isCarry仍然为false;如果上两位有进位,则0+1+1=10会产生进位,则isCarr仍然为true,所以代码如下
isCarry = isCarry;
} else if (aBit + bBit == 98) {// '1'+'1'=98
// 在插入之前,需要先判断上两位相加是否有进位,因为1+1=10已经产生进位,如果上两位有进位则1+1+1=11,则插入'1';如果上两位没有进位则1+1+0=10,则插入'0'
result.insert(0, isCarry ? '1' : '0');
// 设置当前相加(是在已经判断上两位相加的基础上的处理)是否会产生进位,1+1+0=10一定会产生进位,1+1+1也一定会产生进位,所以设置为true
isCarry = true;
}
}
// 第四步,收尾工作,注意需要判断最后是否还存在进位,如果存在则需要添加一个'1'。比如"1010"和"1011"
if (isCarry) {
result.insert(0, '1');
}
return result.toString();
}
/**
* <p>为短的二进制字符串的高位添加0。</p>
* <p>例如:<code>str='1',zeroCount=1 => return '01';</code></p>;
*
* @param str 短的二进制字符串
* @param zeroCount 要在高位添加0的个数
* @return 返回添加0后的二进制字符串
*/
private String addZero(String str, int zeroCount) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < zeroCount; i++) {
sb.append('0');
}
sb.append(str);
return sb.toString();
}
}
解法3
解法3和解法2本质上是一样的,由解法2倒序遍历字符串延申到栈的先进后出特性,从而有如下的代码:
public class Solution {
/**
* <p>思路:栈</p>
* <p>步骤:</p>
* <ul>
* <li>第一步,将两个字符串分别填入两个栈中</li>
* <li>第二步,同时循环遍历两个栈</li>
* <li>第三步,处理两个栈中的剩余元素,其实只有一个栈有剩余元素</li>
* <li>第四步,收尾工作,注意需要判断最后是否还存在进位,如果存在则需要添加一个'1'。比如"1010"和"1011"</li>
* </ul>
* <p>结果:成功</p>
* <ul>
* <li>执行用时:4 ms, 在所有 Java 提交中击败了17.51% 的用户</li>
* <li>内存消耗:38.5 MB, 在所有 Java 提交中击败了27.71% 的用户</li>
* <li>通过测试用例:294 / 294</li>
* </ul>
*
* @param a 第一个二进制字符串
* @param b 第二个二进制字符串
* @return 两个二进制字符串相加的和
*/
public String addBinary(String a, String b) {
// 第一步,将两个字符串分别填入两个栈中
Stack aStack = getStack(a);
Stack bStack = getStack(b);
// 第二步,同时循环遍历两个栈
StringBuilder result = new StringBuilder();// 保存相加结果的字符串
boolean isCarry = false;// 是否进位的标志,true表示两位字符相加存在进位('1'+'1'),false表示不存在进位('0'+'1'或'0'+'0')
while (!aStack.isEmpty() && !bStack.isEmpty()) {
char aBit = (char) aStack.pop();// 获取栈顶元素,并且将栈顶元素出栈
char bBit = (char) bStack.pop();
if (aBit == bBit) {// '0'+'0'=96 或 '1'+'1'=98
result.insert(0, isCarry ? '1' : '0');// 如果上两位相加有进位,则0+0+1=01或1+1+1=11;如果上两位相加没有进位,则0+0+0=00或1+1+0=10
isCarry = aBit == '1';// 至于当前两位相加的结果是否有进位,0+0=0一定没有进位,1+1=10一定有进位
} else {// '0'+'1'=97 或 '1'+'0'=91
result.insert(0, isCarry ? '0' : '1');// 如果上两位相加有进位,则0+1+1=10;如果上两位相加没有进位,则0+1+0=01
// 至于当前两位相加的结果是否有进位,取决于上两位相加是否有进位,所以isCarry不变
}
}
// 第三步,处理两个栈中的剩余元素,其实只有一个栈有剩余元素
while (!aStack.isEmpty()) {
char c = (char) aStack.pop();
if (!isCarry) {// 如果没有进位,则插入原值
result.insert(0, c);
isCarry = false;
} else {// 如果有进位,则如果c为1插入0,如果c为0则插入1
result.insert(0, '1' - c);
isCarry = '1' - c == 0;
}
}
while (!bStack.isEmpty()) {
char c = (char) bStack.pop();
if (!isCarry) {// 如果没有进位,则插入原值
result.insert(0, c);
isCarry = false;
} else {// 如果有进位,则如果c为1插入0,如果c为0则插入1
result.insert(0, '1' - c);
isCarry = '1' - c == 0;
}
}
// 第四步,收尾工作,注意需要判断最后是否还存在进位,如果存在则需要添加一个'1'。比如"1010"和"1011"
if (isCarry) {
result.insert(0, '1');
}
return result.toString();
}
/**
* <p>将字符串中的所有字符填入栈中。</p>
*
* @param str 字符串
* @return 返回填满字符的栈
*/
private Stack getStack(String str) {
char[] chars = str.toCharArray();
Stack stack = new Stack();
for (char aChar : chars) {
stack.push(aChar);
}
return stack;
}
}
以上是关于LeetCode之67.二进制求和的主要内容,如果未能解决你的问题,请参考以下文章