左移(1 << x)
左移操作的结果是将二进制数的每一位都向左移动指定的位数,右边空出的位用 0 填充。左移操作相当于将原始数乘以 2 的 x 次幂。
右移 (x >> 1)
表达式 x >> 1 是右移操作,用于将整数 x 的二进制表示向右移动一位。右移操作可以用于快速将整数除以 2 的幂次方,因为右移一位相当于除以 2。
与(&)
如果两个位都是 1,与的结果为 1。
如果两个位中至少有一个为 0,与的结果为 0。
提取或修改一个数的特定位。 mask & (1 << i)
掩码生成:与操作常用于生成位掩码,用于提取或修改一个数的特定位。
提取mask中下标(从右往左数 0 - n)第 i 下标的数字是0还是1
提取或修改一个数的特定位。 mask >> (i - 1) & 1
i 从0 开始
掩码生成:与操作常用于生成位掩码,用于提取或修改一个数的特定位。
用于提取 mask 中的第 i - 1 下标的值,而不是第 i 下标的值。
和mask & (1 << (i - 1))一样。
提取整数 a(二进制)最右侧(最低位)的 1 所在的位
表达式 a& (-a) 可以用于提取整数 a最右侧(最低位)的 1 所在的位,将其他位清零。这个操作通常用于查找二进制中最右侧的 1 所在的位置,也就是最低有效位(LSB,Least Significant Bit)。
原理
a & (-a) 可以获得a最低的非0位 比如a的二进制原码是 0000 1010,这里最低非0位是bit 1(从右往左第2位)
-a在二进制中的表示是补码(2’s complement code)形式,即先按位取反再加1
取反得 1111 0101(即1's complement code,反码)
加1得 1111 0110(即2's complement code,补码)
原码(0000 1010) 与 补码(1111 0110) 做与运算(&),得 0000 0010,即原码 0000 1010的LSB
更详细的解释:
我们从右向左看发生了什么:
原码最低非0位右边所有的0,经由取反后全部变为1,反码+1会导致这些1逐位发生进位并变为0,最终进位记到最低非0位
原最低非0位是1,取反后是0,进位到这一位0变成1,不再向左进位
原最低非0位左边的每一位经由取反后 和 原码 进行与运算必为0
异或(^)
如果两个位不同,异或的结果为 1。
如果两个位相同,异或的结果为 0。
任何数和 0做异或运算,结果仍然是原来的数,即 a⊕0=a
任何数和其自身做异或运算,结果是 0,即 a⊕a=0
异或运算满足交换律和结合律,即 a⊕b⊕a=b⊕a⊕a=b⊕(a⊕a)=b⊕0=b
交换两个数字
int a = 5, b = 7;
a = a ^ b;
b = a ^ b;
a = a ^ b;
消除重复的数字(只有偶次重复才能用)
通过将数组中的所有数进行异或操作,重复出现的数会相互抵消,最终剩下的数就是单独出现的数。
假设数组中有 2m+1个数,其中有 m个数各出现两次,一个数出现一次。令 a1、a2、…、am为出现两次的 m个数,am+1为出现一次的数。根据交换律和结合律,数组中的全部元素的异或运算结果总是可以写成如下形式:(a1⊕a1)⊕(a2⊕a2)⊕⋯⊕(am⊕am)⊕am+1 = am+1
查找缺失的数字
在一个任选n (0-n) 个不同数的数组中,其中一个数丢失了,数组 nums 中有 n 个数,在这 n 个数的后面添加从 0 到 n 的每个整数,则添加了 n+1个整数,共有 2n+1个整数。
在 2n+1个整数中,丢失的数字只在后面 n+1个整数中出现一次,其余的数字在前面 n 个整数中(即数组中)和后面 n+1个整数中各出现一次,即其余的数字都出现了两次。因此对上述 2n+1个整数进行按位异或运算,结果即为丢失的数字。