bit操作
& 符号,x & y ,会将两个十进制数在二进制下进行与运算
| 符号,x | y ,会将两个十进制数在二进制下进行或运算
^ 符号,x ^ y ,会将两个十进制数在二进制下进行异或运算
<< 符号,x << y 左移操作,最右边用 0 填充
>> 符号,x >> y 右移操作,最左边用 0 填充
~ 符号,~x ,按位取反操作,将 x 在二进制下的每一位取反
整数集合set位运算
整数集合做标志时,比如回溯时的visited标志数组
vstd 访问 i :vstd | (1 << i)
vstd 离开 i :vstd & ~(1 << i)
vstd 不包含 i : not vstd & (1 << i)
并集 :A | B
交集 :A & B
全集 :(1 << n) - 1
补集 :((1 << n) - 1) ^ A
子集 :(A & B) == B
判断是否是 2 的幂 :A & (A - 1) == 0
最低位的 1 变为 0 :n &= (n - 1)
最低位的 1:A & (-A),最低位的 1 一般记为 lowbit(A)
消除二进制数中最后出现的 1
n & (n - 1)
只要每次执行这个操作,就会消除掉 n 的二进制中 最后一个出现的 1。因此执行 n & (n - 1) 使得 n 变成 0 的操作次数,就是 n 的二进制中 1 的个数。
Java 中的 算术右移 和 逻辑右移
算术右移 >> :舍弃最低位,高位用符号位填补;
逻辑右移 >>> :舍弃最低位,高位用 0 填补。
那么对于一个负数n而言,其二进制最高位是 1,如果使用算术右移不断更新n,(n >>= n),那么n高位填补的仍然是 1。也就是 n 永远不会为 0。
1. 算术左移,逻辑左移
算术左移和逻辑左移一样都是右边补0: 比如 00101011
算术左移一位:01010110
逻辑左移一位:01010110
对于二进制的数值来说左移n位等于原来的数值乘以2的n次方 。比如00011010十进制是26,左移两位后是01101000转成十进制是104恰好是26的4倍。
ps:这种倍数关系只适用于左移后被舍弃的高位不含1的情况,否则会溢出。
2. 算术右移,逻辑右移
逻辑右移很简单,只要将二进制数整体右移,左边补0即可 。
如10101101逻辑右移一位为01010110
算术右移符号位要一起移动,并且在左边补上符号位,也就是如果符号位是1就补1符号位是0就补0 ,比如:11100算术右移一位为11110(符号位1跟着一起移动并且左边补了1)。
对于二进制的数值来说右移n位等于原来的数值除以2的n次方
比如10110100十进制是76(需要先将这个补码转换成原码之后再转换成十进制),右移两位后是11101101转成十进制是19恰好是76的4倍。
算术左移和算术右移主要用来进行有符号数的倍增、减半;
逻辑左移和逻辑右移主要用来进行无符号数的倍增、减半。
移位操作是计算机指令中比较基本的操作,是位运算的一种。在移位运算时,byte、short和char类型移位后的结果会变成int类型,对于byte、short、char和int进行移位时,编译器未做任何优化的情况下(优化后不可预期),规定实际移动的次数是移动次数和32的余数,也就是移位33次和移位1次得到的结果相同。
移动long型的数值时,规定实际移动的次数是移动次数和64的余数,也就是移动66次和移动2次得到的结果相同。