专栏原创出处:github-源笔记文件 ,github-源码 ,欢迎 Star,转载请附上原文出处链接和本声明。
有哪些位运算
&
与运算 两个位都是 1 时,结果才为 1,否则为 0|
或运算 两个位都是 0 时,结果才为 0,否则为 1^
异或运算,两个位相同则为 0,不同则为 1~
取反运算,0 则变为 1,1 则变为 0<<
左移运算,向左进行移位操作,高位丢弃,低位补 0(部分语言支持无符号左移<<<
)>>
右移运算,向右进行移位操作,对无符号数,高位补 0,对于有符号数,高位补符号位(部分语言支持无符号右移>>>
)
位移实现乘除法
int a = 2;
a >> 1; ---> 1
a << 1; ---> 4
a ^ b ^ b = a
异或交换两数
// 案例:数字替换
//普通操作
void swap(int &a, int &b) {
a = a + b;
b = a - b;
a = a - b;
}
//位与操作
void swap(int &a, int &b) {
a ^= b; // a = (a^b);
b ^= a; // b^(a^b) --> b = (b^b)^a = a
a ^= b; // (a^b)^a = (a^a)^b = b
}
应用:
- 数组中,只有一个数出现一次,剩下都出现两次,找出出现一次的数
- 数组中,只有一个数出现一次,剩下都出现三次,找出出现一次的
- 数组中,只有两个数出现一次,剩下都出现两次,找出出现一次的
与运算判断奇偶数
只要根据数的最后一位是 0 还是 1 来决定即可,为 0 就是偶数,为 1 就是奇数。
0 == (a & 1) 偶数
1 == (a & 1) 奇数
取反运算交换符号
交换符号将正数变成负数,负数变成正数。
整数取反加 1,正好变成其对应的负数 (补码表示);负数取反加一,则变为其原码,即正数
int reversal(int a) {
return ~a + 1;
}
位操作求绝对值
整数的绝对值是其本身,负数的绝对值正好可以对其进行取反加一求得,即我们首先判断其符号位(整数右移 31 位得到 0,负数右移 31 位得到 -1,即 0xffffffff),
然后根据符号进行相应的操作。
int abs(int a) {
int i = a >> 31;
return i == 0 ? a : (~a + 1);
}
上面的操作可以进行优化,可以将 i == 0 的条件判断语句去掉。我们都知道符号位 i 只有两种情况,即 i = 0 为正,i = -1 为负。
对于任何数与 0 异或都会保持不变,与 -1 即 0xffffffff 进行异或就相当于对此数进行取反,因此可以将上面三目元算符转换为 ((a^i)-i)。
- 整数时 a 与 0 异或得到本身,再减去 0
- 负数时与 0xffffffff 异或将 a 进行取反,然后在加上 1,即减去 i(i=-1)
int abs2(int a) {
int i = a >> 31;
return ((a^i) - i);
}
位操作进行高低位交换
给定一个 16 位的无符号整数,将其高 8 位与低 8 位进行交换,求出交换后的值。
只要将无符号数 a>>8 即可得到其高 8 位移到低 8 位,高位补 0;将 a<<8 即可将 低 8 位移到高 8 位,低 8 位补 0,然后将 a>>8 和 a<<8 进行或操作既可求得交换后的结果。
a = (a >> 8) | (a << 8);
消去二进制最后一位的 1
x & (x - 1)
用于消去 x 最后一位的 1
x = 1100
x - 1 = 1011
x & (x - 1) = 1000
// 案例:位操作统计二进制中 1 的个数
int count = 0
while(a != 0){
a = a & (a - 1); // 每次计算 a 少一个 1
count++;
}
应用:
- 检测 n 是否为 2 的幂次。(大于 0,二进制只有一个 1)
- 位操作统计二进制中 1 的个数(每次消去一个,消到为 0)
参考
更多相关专栏内容汇总:
