基础
我们假设
a
=
1000110
1
(
2
)
,
b
=
0010011
1
(
2
)
a=10001101_{(2)}, b=00100111_{(2)}
a=10001101(2),b=00100111(2)
为了表示方便下面的数统一用二进制表示(如果是其他形式会标注)
a&b
&——按位与,将a和b每个二进制位与,如
a a a | 1000 1101 |
---|---|
b b b | 0010 0111 |
a & b a\And b a&b | 0000 0101 |
即 :
0 & 0 = 0 0\And 0=0 0&0=0 | 0 & 1 = 0 0\And 1=0 0&1=0 |
---|---|
1 & 1 = 1 1\And 1 = 1 1&1=1 | 1 & 0 = 0 1\And 0=0 1&0=0 |
a|b
|——按位或,将a和b每个二进制位或,如
a a a | 1000 1101 |
---|---|
b b b | 0010 0111 |
a ∣ b a\mid b a∣b | 1010 1111 |
即 :
0 ∣ 0 = 0 0\mid 0=0 0∣0=0 | 0 ∣ 1 = 1 0\mid 1=1 0∣1=1 |
---|---|
1 ∣ 1 = 1 1\mid 1 = 1 1∣1=1 | 1 ∣ 0 = 1 1\mid 0=1 1∣0=1 |
a^b
^——按位异或,将a和b每个二进制位异或
其实异或这玩意就是(~a & b)|(a &~b),如:
|——按位或,将a和b每个二进制位或,如
a a a | 1000 1101 |
---|---|
b b b | 0010 0111 |
a ∧ b a \land b a∧b | 1010 1010 |
即 :
0 ∧ 0 = 0 0\land 0=0 0∧0=0 | 0 ∧ 1 = 1 0\land 1=1 0∧1=1 |
---|---|
1 ∧ 1 = 0 1\land 1 = 0 1∧1=0 | 1 ∧ 0 = 1 1\land 0=1 1∧0=1 |
~a
~——按位取反,将a的每个二进制位按位取反,如:
a a a | 1000 1101 |
---|---|
∼ a \sim a ∼a | 0111 0010 |
即 :
∼ 0 = 1 \sim 0=1 ∼0=1 | ∼ 1 = 0 \sim1=0 ∼1=0 |
---|
a<<k
<<——左移运算符,将a左移k个二进制位,相当于乘以 2 k 2^k 2k
a a a | 1000 1101 |
---|---|
a ≪ 2 a\ll 2 a≪2 | 0011 0100 |
可以看出高位被移出去了,即溢出
即 :
1 ≪ 2 = 100 1\ll 2=100 1≪2=100 | 0 ≪ 2 = 0 0\ll2=0 0≪2=0 |
---|
a>>k
相当于除以 2 k 2^k 2k
算数右移(考虑符号位)
每一次右移的最高位按当前符号位填充
a a a | 1000 1101 | − 11 5 ( 10 ) -115_{(10)} −115(10) |
---|---|---|
a ≫ 2 a\gg 2 a≫2 | 1110 0011 | − 2 9 ( 10 ) -29_{(10)} −29(10) |
逻辑右移(不考虑符号位)
最高位直接填充0
a a a | 1000 1101 | 14 1 ( 10 ) 141_{(10)} 141(10) |
---|---|---|
a ≫ 2 a\gg 2 a≫2 | 0010 0011 | 3 5 ( 10 ) 35_{(10)} 35(10) |
数字都给你们了,简单除一下就能验证了
我们都知道计算机内部二进制最高位为符号位,且用补码表示
看起来逻辑右移好像有一丢丢不靠谱,可爱的小a酱逻辑右移两位最高位都变了||ヽ( ̄▽ ̄)ノミ|Ю
所以,我们聪明的编译器当然会做一些小处理撒:
数据类型为unsigned时,为逻辑右移
数据类型为signed时,为算数右移
应用
交换两数
a ^= b;
b ^= a;
a ^= b;
让我们小小的分析一下⁄(⁄ ⁄•⁄ω⁄•⁄ ⁄)⁄
设x=a,y=b;
a ^= b | a = a ^ b | a = x ^ y |
---|---|---|
b ^= a | b = a ^ b | b = x ^ y ^ y = x |
a ^= b | a = a ^ b | a = x ^ y ^ x = y |
当然这只是秀下技巧,实际效率反而没有直接交换两数高哦(^ ^)
位运算加法
a
=
(
a
n
a
n
−
1
.
.
.
a
2
a
1
a
0
)
(
2
)
a=(a_n a_{n-1}...a_2 a_1 a_0)_{(2)}
a=(anan−1...a2a1a0)(2)
b
=
(
b
n
b
n
−
1
.
.
.
b
2
b
1
b
0
)
(
2
)
b=(b_n b_{n-1}...b_2 b_1 b_0)_{(2)}
b=(bnbn−1...b2b1b0)(2)
c
=
(
c
n
c
n
−
1
.
.
.
c
2
c
1
c
0
)
(
2
)
c=(c_nc_{n-1}...c_2c_1c_0)_{(2)}
c=(cncn−1...c2c1c0)(2)
设
c
i
c_i
ci 为
a
i
a_i
ai 和
b
i
b_i
bi 的进位
我们很容易看出第
c
i
+
1
c_{i+1}
ci+1 由
a
i
,
b
i
,
c
i
a_i,b_i,c_i
ai,bi,ci推出
核心代码:
while (b) {
int r = (a&b) << 1;
a ^= b;
b = r;
}
快速幂
戳一下ヾ(◍°∇°◍)ノ゙
里面的普通版就是快速幂啦
数组中唯一出现过一次的数
找数组中唯一出现过一次的数,数据保证数组其他数出现有且仅有两次
♪(^ ∇^*)把数组中所有数都异或一下,返回异或和
我们知道1 ^ 1 = 0, 0 ^ 0 = 0,也就是一个数与自己异或结果为0,那最终答案就是与0异或,是自己本身。
可以继续推广一下:
一个数奇数次异或为自己本身,偶数次异或为0
线性基
使n个整数异或和最大,时间复杂度
O
(
n
)
O(n)
O(n)
传送门