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.二进制求和的主要内容,如果未能解决你的问题,请参考以下文章

Leetcode67. 二进制求和(简单模拟)

LeetCode 67. 二进制求和 | Python

[leetcode] 67. 二进制求和

leetcode-67.二进制求和

LeetCode67. 二进制求和

leetcode算法67.二进制求和