https://blog.csdn.net/pmdream/article/details/107066488
主要针对这个算法。
int k = 0; for(int num: nums) { k ^= num; } //获得k中最低位的1 int mask = 1;
//mask = k & (-k) 这种方法也可以得到mask while((k & mask) == 0) { mask <<= 1; }
为什么这个方法也可以得到mask呢?
因为有符号的话,-k 可以认为最高位符号位为1,
那么做与运算的时候,
负数的二进制表示,其实是负数的源码 除符号位取反,然后 +1.就是负数的补码就是真正的二进制标识,
那么k & (-k)画一下就是了~
负数的二进制表示请戳这里
二进制正数的符号位为0、负数的符号位为1
&:按位与。
|:按位或。
~:按位非。
^:按位异或。
<<:左位移运算符。
>>:右位移运算符。
<<<:无符号右移运算符。
按位与的运算规则
操作数1
0
0
1
1
操作数2
0
1
0
1
按位与
0
0
0
1
规则总结:只有两个操作数对应位同为1时,结果为1,其余全为0. (或者是只要有一个操作数为0,结果就为0)。
按位或的运算规则
操作数1
0
0
1
1
操作数2
0
1
0
1
按位或
0
1
1
1
规则总结:只有两个操作数对应位同为0时,结果为0,其余全为1.(或者是只要有一个操作数为1,结果就为1)。
按位非的运算规则
操作数
0
1
按位或
1
0
在求负数的源码中使用过。
相当于取反
按位异或的运算规则
操作数1
0
0
1
1
操作数2
0
1
0
1
按位异或
0
1
1
0
规则总结:异:1.
符号位不变,低位补0。如:2<<2结果为8。
当移动的位数超过数字本身的位数时,那么不就都需要补0操作,实际上不是的,java不可能做那么浪费资源的事情。在真正执行位移前,其对要移动的位数做了一些预处理,比如32处理为0,-1处理为31.
低位溢出,符号位不变,并用符号位补溢出的高位。如:-6>>2结果为-2。
低位溢出,高位补0。注意,无符号右移(>>>)中的符号位(最高位)也跟着变,无符号的意思是将符号位当作数字位看待。如:-1>>>1结果为2147483647。这个数字应该比较熟悉,看两个输出语句就知道是什么了:
System.out.println(Integer.toBinaryString(-1>>>1));
System.out.println(Integer.toBinaryString(Integer.MAX_VALUE));
输出结果为:
1111111111111111111111111111111
1111111111111111111111111111111
-1>>>1竟然得到了int所能表示的最大整数,精彩。
参考:https://www.cnblogs.com/findbetterme/p/10787118.html
二进制转十进制
负数的二进制表示,是以补码的形式存在的,
什么叫做补码?原码,反码?
原码:一个整数,按照绝对值大小转换成的二进制数,称为原码。 比如 00000000 00000000 00000000 00000101 是 5的 原码。
反码:将二进制数按位取反,所得的新二进制数称为原二进制数的反码。 取反操作指:原为1,得0;原为0,得1。(1变0; 0变1) 比如:将00000000 00000000 00000000 00000101每一位取反,得11111111 11111111 11111111 11111010。
补码:反码加1称为补码。 也就是说,要得到一个数的补码,先得到反码,然后将反码加上1,所得数称为补码。 比如:00000000 00000000 00000000 00000101 的反码是:11111111 11111111 11111111 11111010。
也就是想得到一个负数的表示,
先得到这个数的反码,然后+1 得到的就是补码。
整数-1在计算机中如何表示。 假设这也是一个int类型,那么:
1、先取1的原码:00000000 00000000 00000000 00000001 2、得反码: 11111111 11111111 11111111 11111110 3、得补码: 11111111 11111111 11111111 11111111
可见,-1在计算机里用二进制表达就是全1。16进制为:0xFFFFFF
作者:Jfeng666 链接:https://www.jianshu.com/p/e3b20e76af1f