LeetCode371: 求两整数之和

题目:定义一个函数,在该函数中可以实现任意两个整数的加法。

知识准备:

虽然这道题的解法众多,但我主要想针对位运算做解释(以下的解释主要针对Java)。

在解题之前,首先要了解在Java里,一个整数(int)由32个bits组成(bit0-31)。Bit0如果为0,则此整数为正数。如果bit01,则此整数为负数。那么问题来了,如何用二进制表示-1

1的二进制为00...01,其补码(~1)11...10(也就是把1变成0,0变成1)。然后再把补码加1,即得到-1的二进制,也就是11...11
同理,2的二进制为00...10,其补码(~2)11...01。然后再把补码加1,即得到-2的二进制,也就是11...10

在Java中,最大的正整数为01...11(bit0为0,bit1-31为1),用十六进制表示为0x7FFF FFFF,用十进制表示为2147483647
如果把01...11加1则变成了10...00,用十六进制表示为0x8000 0000,用十进制表示为-2147483648

解题思路:

现在回到这题,让我们假设有两个整数615,二进制分别表示为00...011000...1111
如果我们对这两个二进制做异或运算(xor, ^),可以得到:

  0110
  1111 xor
  ----
  1001

用res代表这个xor的结果。

如果我们对这两个二进制做与运算(and, &),可以得到:

  0110
  1111 and
  ----
  0110

把与运算的结果左移一位(<<1),则可以得到进位,也就是1100。用carry代表这个左移的and的结果。

重新开始异或运算,这次是在res和carry之间:

1001 ^ 1100 = 0101,重新赋值给res

重新开始与运算且把结果左移一位:

(1001 & 1100)  << 1 = 1000 << 1 = 10000,重新赋值给carry

重复做异res和carry的异或和与运算,直到carry变成0为止:

00101 ^ 10000 = 10101,重新赋值给res
(00101 & 10000) << 1 = 00000 << 1 = 00000,重新赋值给carry

至此,运算结束。我们可以看到res为10101,用十进制表示为21,是165的值。

这个逻辑同样可以运用到负数运算中。

不过,这道题其实并没有考虑到大数处理的问题。比如说如果求2147483647(INT_MAX)2147483647的和,按照上述逻辑,最后的答案会是-2。是因为:

01...11
01...11 +
--------
11...10

用十进制表达也就是-2。

根据此逻辑,不难写出Java代码:

public int getSum(int a, int b) {
    while (b != 0) {
        int res = a^b;
        int carry = (a&b)<<1;
        a = res;
        b = carry;
    }
    return res;
}

如果用递归表达:

public int getSum(int a, int b) {
    // if (a == 0) return b;
    if (b == 0) return a;
    return getSum(a^b, (a&b) << 1);
}

你可能感兴趣的:(LeetCode371: 求两整数之和)