【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
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

我的思路:

起初我就否定了将值转换为10进制的整型然后进行加法运算,再转换为二进制字符串获得结果的方法,一是因为基础知识不够扎实,对于这些进制转换记的不是很牢固,不想去扒进制转换的方法。二是因为我心里想着怎么这也是一个算法题,不会那么简单,而且即使可以,那样做的意义也不大。所以就想通过位运算的方法获得结果。后面看了题解之后发现使用位运算而不是进制转换直接相加还有更深层次的好处,这个放到题解后的思路部分来说。这里先说我位运算的思路。二进制每一位都是0或者1.那么两个数值对应的位值相加,有以下几种可能:0-0组合得出结果为0进位为0,1-0组合结果为1进位为0,1-1组合结果为1进位为1,即可以得到结果位值是两个位值之和除以2的运算结果,进位则为两个位值之和除以2的余数。这样就简单的,我只需要从最后一位往前遍历运算,将结果拼接成字符串,然后在反转展示即可。同时还有两个问题,问题一是两个二进制数的位数可能不一致。那么位数不够的那么怎么补0呢?一个方案是将二进制数字符串切割成char数组,然后进行填充短位不足的部分为0到长位长度的操作。一个方法是在运算过程中进行判断,如果位标已超出长度限制,则用0进行计算。后面解决方法我选择了后者,仅仅是因为我觉得这样写着简单一些…。问题二是怎么将char转为int以进行加除运算,我起初想过将char转换为String(因为char无法使用Integer.parseInt),再用Integer.parseInt来转换为int。但是这样颇为复杂。后面我忽然想起来char其实是可以进行数学运算的,char的数学运算其实是其ASCII值的运算(是的吧,这个只有一个模糊的概念,如果不对欢迎指正)。我们将char转换为int只需用 char-'0’即可,这样方便的多,遂采用这个方法。

我的代码:

public String addBinary(String a, String b) {
     
		// 算出最大长度以遍历
        int maxLength = Math.max(a.length(), b.length());
        int i = 1;
        // 进位值
        int carryVal = 0;
        StringBuilder stb = new StringBuilder();
        while (i <= maxLength) {
     
        	// a的位值
            int aIndexVal = i > a.length() ? 0 : a.charAt(a.length()-i) -'0';
            // b的位值
            int bIndexVal = i > b.length() ? 0 : b.charAt(b.length()-i) -'0';
            // 当前位的计算结果为a位值+b位值+之前的进位值
            int indexVal = aIndexVal + bIndexVal + carryVal;
            // 拼接当前位的实际值
            stb.append(indexVal%2);
            // 当前位的进位
            carryVal = indexVal/2;
            i++;
        }
        // 如果最后的进位值是1就需要把这个1拼上去
        if (carryVal == 1) {
     
            stb.append(carryVal);
        }
        // 反转后转为字符串返回
        return stb.reverse().toString();
    }

运行结果:
在这里插入图片描述

官方题解:

题目分析
考虑一个最朴素的方法:先将 a a a b b b 转化成十进制数,求和后再转化为二进制数。利用 Python 和 Java自带的高精度运算,我们可以很简单地写出程序:
如果 a a a 的位数是 n n n b b b 的位数为 m m m,这个算法的渐进时间复杂度为 O ( n + m ) O ( n + m ) O(n + m)O(n+m) O(n+m)O(n+m)。但是这里非常简单的实现基于 Python 和 Java 本身的高精度功能,在其他的语言中可能并不适用,并且在 Java 中:
如果字符串超过 33 位,不能转化为 Integer 如果字符串超过 65 位,不能转化为 Long 如果字符串超过500000001 位,不能转化为 BigInteger。因此,为了适用于长度较大的字符串计算,我们应该使用更加健壮的算法。

方法一:模拟
思路和算法
我们可以借鉴「列竖式」的方法,末尾对齐,逐位相加。在十进制的计算中「逢十进一」,二进制中我们需要「逢二进一」。
具体的,我们可以取 n = m a x { ∣ a ∣ , ∣ b ∣ } n=max\{∣a∣,∣b∣\} n=max{ a,b},循环 n n n 次,从最低位开始遍历。我们使用一个变量 c a r r y carry carry 表示上一个位置的进位,初始值为 0 0 0。记当前位置对其的两个位为 a i a_i ai b i b_i bi,则每一位的答案为 ( c a r r y + a i + b i )   m o d   2 ({\rm carry} + a_i + b_i) \bmod{2} (carry+ai+bi)mod2,下一位的进位为 ⌊ c a r r y + a i + b i 2 ⌋ ⌊ \frac{ {\rm carry} + a_i + b_i}{2}⌋ 2carry+ai+bi。重复上述步骤,直到数字 a a a b b b 的每一位计算完毕。最后如果 c a r r y carry carry 的最高位不为 0 0 0,则将最高位添加到计算结果的末尾。
注意,为了让各个位置对齐,你可以先反转这个代表二进制数字的字符串,然后低下标对应低位,高下标对应高位。当然你也可以直接把 a a a b b b 中短的那一个补 0 0 0 直到和长的那个一样长,然后从高位向低位遍历,对应位置的答案按照顺序存入答案字符串内,最终将答案串反转。
复杂度分析
假设 n = max ⁡ { ∣ a ∣ , ∣ b ∣ } n = \max\{ |a|, |b| \} n=max{ a,b}
时间复杂度: O ( n ) O(n) O(n),这里的时间复杂度来源于顺序遍历 a a a b b b
空间复杂度: O ( 1 ) O(1) O(1),除去答案所占用的空间,这里使用了常数个临时变量。

方法二:位运算
思路和算法
如果不允许使用加减乘除,则可以使用位运算替代上述运算中的一些加减乘除的操作。
a a a b b b 转换成整型数字 x x x y y y,在接下来的过程中, x x x 保存结果, y y y 保存进位。
当进位不为 0 0 0
计算当前 x x x y y y 的无进位相加结果:answer = x ^ y
计算当前 x x x y y y 的进位:carry = (x & y) << 1
完成本次循环,更新 x = answer,y = carry
返回 x x x 的二进制形式
为什么这个方法是可行的呢?在第一轮计算中,answer 的最后一位是 x x x y y y 相加之后的结果,carry 的倒数第二位是 x x x y y y 最后一位相加的进位。接着每一轮中,由于 carry 是由 x x x y y y 按位与并且左移得到的,那么最后会补零,所以在下面计算的过程中后面的数位不受影响,而每一轮都可以得到一个低 i i i 位的答案和它向低 i + 1 i + 1 i+1 位的进位,也就模拟了加法的过程。

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/add-binary/solution/er-jin-zhi-qiu-he-by-leetcode-solution/
来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

官方题解下的其它联想:

首先说一下我看了题解之后,发现我不选择进制转换直接相加更深层次的好处就是进制转换直接相加无法计算超大数值,如果给的二进制字符串已经超过Java定义的数据类型所能容纳的大小,就会出现无法计算的情况。所以针对这个题目,最好的方法依然是使用模拟运算的方法。题解下的三种方式,其中直接进制转换已没必要贴出来,方法一模拟运算的实现方式即为上面我的题解。至于方法二替换位运算法:对于Java来说依然无法摆脱给的二进制字符串已经超过Java定义的数据类型所能容纳的大小从而导致无法运算的情况,但是方法二也是我之前没接触的一个方法,值得深究一下。探究之后得出方法二用我的话描述就是:首先要明白 a^b 运算得出的结果就是a+b各个位不进行进位运算相加的结果,而a&b运算得出的结果就是a+b运算各位所产生的进位。而加法运算可以分解为不进行进位运算相加的结果加上各位的得到的进位,即a+b = (a ^ b) + (a&b) <<1。a&b向左移一位是因为我们的进位都是加到其前一位的,而左移一位后最后一位补0,不会影响加操作的结果。(a ^ b) + (a&b) <<1的加操作还可能有进位,那么我们要做的就是将新获得的值与新活动的进位再进行相加,一直加到不再有进位,即进位为0.此时得到的结果就是a+b的运算结果。根据这个思路可以得出另外一种解法:

class Solution {
     
    public String addBinary(String a, String b) {
     
        int aVal = Integer.parseInt(a, 2);
        int bVal = Integer.parseInt(b, 2);
        // 未进位的加和
        int result = aVal ^ bVal;
        // 进位的值
        int carry = (aVal & bVal) << 1;
        // 进位不为0
        while (carry != 0) {
     
        	// 暂记当前的未进位的加和
            int lastIndexResult = result;
            // 计算再次相加的未进位加和
            result = lastIndexResult ^ carry;
            // 计算再次相加的进位
            carry = (lastIndexResult & carry) <<1;
        }
        return Integer.toBinaryString(result);
    }
}

运行结果:
在这里插入图片描述
可以看到执行出错了,就是因为因为给出的二进制字符串太大已经超过了int数据类型所能容纳的大小了。但是仍然不失为一种别致的解题思路。

你可能感兴趣的:(LeetCode,算法,JAVA,算法,java)