位运算有
-
&
按位与 -
|
按位或 -
^
按位异或 -
>>n
右移n位 -
<
左移n位
参考:
https://zhuanlan.zhihu.com/p/37167219
https://www.cnblogs.com/lmsthoughts/p/6659571.html
在int为32位的语言中,有一些计算的小技巧
1、int最大值
(1<<31)-1
2、int最小值
1<<31
3、取绝对值
a ^ (a>>31) - (a>>31);
4、全1
~0 = -1 = 0xffffffff(1个f 4位,8个f 32位)
5、负数
-a = ~a + 1
一些性质
a对其自身运算
a & a = a
a | a = a
a ^ a = 0 (去重常用)a对0运算
a & 0 = 0
a | 0 = a
a ^ 0 = a (置换常用)a对(~0)运算
a & (~0) = a
a | (~0) = ~0
a ^ (~0) = ~a (取反的另一种写法)通过位运算交换两个数
a ^= b;
b ^= a;
a ^= b;
位与操作解释:第一步:
a ^= b ---> a = (a^b)
;
第二步:b ^= a ---> b = b^(a^b) ---> b = (b^b)^a = a
第三步:a ^= b ---> a = (a^b)^a = (a^a)^b = b
判断奇偶(取出最后一位)
a & 1 等价于 a % 2(结果等于0为偶,位运算效率高)判断符号是否相同
a ^ b >= 0取出第i+1位
a & (1<i+1位 置1
a |=1<i+1位 置0
a &=~(1<i+1位 反转
a ^ (1<在对应i+1位,插入b的对应位;
a |=1< a & (b & 1< (置1后相与=置0后相或)保留最后i-1位;
a & ((1<清零最后i-1位;
a & ~((1<删除最后的1;
a & (a-1) (可用于判断2的幂数)仅保留最后一个1;
a & (-a)得到最高位的1;
a = a |(a>>1);
a = a |(a>>2);
a = a |(a>>4);
a = a |(a>>8);
a = a |(a>>16);
return (a+1)>>1;
异或的一些性质
x^0 = x
x^x = 0
- 交换律:
x^y = y^x
- 结合律:
(x^y)^z = x^(y^z)
- 自反性:
x^y^y = x
其中-x^0 = x
、x^x = 0
、x^y^y = x
很常在位运算的题中用到
A集合里拿掉数x得到B集合,求x
令XOR(X)
表示将X集合内所有的数做异或
XOR(B) ^ XOR(A) = XOR(B) ^ XOR(B) ^ x = 0 ^ x = x
A集合里拿掉数x、y得到B集合,求x和y
首先按上一个的办法
xor(A) ^ xor(B) = xor(B) ^ xor(B) ^ x ^ y = 0 ^ x ^ y = x ^ y
x ^ y
的二进制结果,第n位为1,说明x和y的第n位不相同
根据第n位是否为0把A里所有的数分成A1和A0两个数组(A1里的数的二进制第n位都是1,A0都是0)
A1和A0应该各包含了a或者b(这样第n位才能异或出1),同理可以把B分成B1和B0两个数组
可以得到第一个数 x = A1^B1
第二个数可以y = A0^B0
,当然也可以用x^y^x
求得
另外如果x^y为0
,即x == y
,令SUM(X)为X集合内所有数求和
(SUM(A) - SUM(B)) / 2 = x
集合A里只有数x出现1次,其余数全都重复出现2次,求x
xor(A) = x^y^y^…^z^z = x^(y^y^…^z^z) = x^0 = x
集合A里只有数x出现1次,其余数全都重复出现3次,求x
xor的本质相当于“按位模2加”(adding modulo 2),令p1,p2…pn为布尔值,true为1、false为0,(+)表示异或操作。
p1 (+) p2 (+) ... (+) pn == ( p1 + p2 + ... + pn ) % 2
所以只需要实现按位模3加
( p1 + p2 + ... + pn ) % 3
将集合中所有数二进制表示的同一位的0或1相加,最终的和对3去摸,得到的数即是x
如 A = {5, 7, 7, 7},二进制表示两个数
101
111
111
+ 111
------
434
% 3
------
101
将每个数想象成32位的二进制,对于每一位的二进制的1和0累加起来必然是3N或者3N+1, 为3N代表目标值在这一位没贡献,3N+1代表目标值在这一位有贡献(=1),然后将所有有贡献的位|起来就是结果。这样做的好处是如果题目改成K个一样,只需要把代码改成cnt%k。
res,flag = 0 ,0
for n in nums:
if n < 0:
flag += 1
flag %= 3
for i in range(32):
mask = 1 << i
cnt = 0
for num in nums:
if mask & num:
cnt += 1
if cnt % 3:
res |= mask
python下这个方法遇到负数时需要改一下,可以先计算负数的个数,然后将全部数转成绝对值进行计算
没有^操作时候实现异或,只用&与 |或 ~非
公式很多 如
x ^ y == (~x & y) | (x & ~y)
消去x最后一位的1
x & (x - 1)
x = 1100
x - 1 = 1011
x & (x - 1) = 1000