算法通关村第十一关——位运算实现加法

77 阅读2分钟

代码

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

画图直观理解

🔑用显微镜放大时间轴
低位进位对高位的影响不是一蹴而就,而是一步一步蝴蝶效应形成的。
一个 timestamp只能从低位向相邻的前一高位产生影响。
即下面每张图代表一个时间戳,由2个箭头所指的二进制数形成上方的一个进位(与)和下方的一个非进位(异或)

timestamp 1

image.png

timestamp 2

image.png

timestamp 3

image.png

timestamp 4

image.png

这段代码如何计算和(文字)

代码中的 while 循环会一直执行,直到没有更多的进位需要处理。在每次循环中:

  • 使用按位与(&)和左移(<<)计算当前的进位。
  • 使用按位异或(^)计算加上两个数但不包括进位的结果。
  • 将进位赋值给 b,将不包括进位的加法结果赋值给 a
  • b 变为0,意味着没有更多的进位需要加上,a 中存储了完整的加法结果。

例如:

假设 a = 5(二进制 0101)和 b = 3(二进制 0011):

  1. 计算进位:sign = (0101 & 0011) << 1 -> 0001 << 1 -> 0010
  2. 加上数但不包括进位:a = 0101 ^ 0011 -> 0110(即十进制的6)
  3. 为下一次循环更新 bb = 0010

再次运行循环:

  1. 计算进位:sign = (0110 & 0010) << 1 -> 0010 << 1 -> 0100
  2. 加上数但不包括进位:a = 0110 ^ 0010 -> 0100(即十进制的4)
  3. 为下一次循环更新 bb = 0100

最后一次循环:

  1. 计算进位:sign = (0100 & 0100) << 1 -> 0100 << 1 -> 1000
  2. 加上数但不包括进位:a = 0100 ^ 0100 -> 0000(即十进制的0)
  3. 为下一次循环更新 bb = 1000

因为 b 不为0,所以我们再次循环:

  1. 计算进位:sign = (0000 & 1000) << 1 -> 0000 << 1 -> 0000
  2. 加上数但不包括进位:a = 0000 ^ 1000 -> 1000(即十进制的8)
  3. 为下一次循环更新 bb = 0000

现在 b 为0,循环结束,a1000(即十进制的8),这是5和3相加的正确结果