原文链接地址:https://blog.csdn.net/u013074465/article/details/46686881
https://www.zhihu.com/question/38206659 九章算法一答
一、位运算基本操作
& | 与 | 1 & 1 = 1;1 & 0 = 0;0 & 0 = 0 只有当两个位都为1时,结果为1 |
| | 或 | 1 | 1 = 1;1 | 0 = 1;0 | 0 = 0 只有两个位都为0时,结果为0 |
^ | 异或 | 1 ^ 1 = 0;1 ^ 0 = 1;0 ^ 0 = 0 两个位相同时为0,相异时为1 |
~ | 取反 | 0变1,1变0 |
<< | 左移 | 各二进位全部左移若干位,高位丢弃,低位补0 |
>> | 右移 | 二进位全部右移若干位,对无符号数,高位补0; 有符号数,各编译器处理方法不一样,有的补符号位(算术右移),有的补0(逻辑右移) |
1、以上运算符,仅~是单目运算符,其他都是双目运算符。
2、位操作仅针对整型数据,对float、double进行位操作会出错。
3、VS2010 和GCC均采用算术右移:对负数右移,会在其高位补符号位,也就是补1。
4、还有其他复合位操作: &= |= <<= >>=
二、位操作的运用
1、奇偶性的判断
对于二进制表示来说,其最右位为1则该数为奇数,最右位为0则该数为偶数。
- int a;
- cin >> a;
- if (a & 1)
- cout << "奇数" << endl;
- if ((a & 1) == 0) //注意:&的优先级低于==的优先级,因此if内部与运算必须加括号!
- cout << "偶数" << endl;
2、 不借助变量交换两个数
//B方法
void Swap(int &a, int &b) {
a ^= b; b ^= a; a ^= b;
}
a = a ^ b;
b = a ^ b; //那么 b = a ^ b = (a ^ b) ^ b = a ^ (b ^ b) = a ^ 0 = a,即将a赋值给了b
a = a ^ b; //那么 a = a ^ b = a ^ (a ^ b) = (a ^ a) ^ b = 0 ^ b = b,即实现了将b赋值为a
3、取相反数
也就是将正数n变为-n,将负数n变为-n。
将整数取反加1即可得到其相反数:
例如,32位系统中正数13(二进制表示为00000000 00000000 00000000 00001101),
取反为(11111111 11111111 11111111 11110010),
然后再加1为(11111111 11111111 11111111 11110011),计算机中数是用二进制的补码表示的,
因此取反加1的结果(11111111 11111111 11111111 11110011)即为-13的二进制补码表示形式。
常见的一个等式:-n = ~(n - 1) = ~n + 1
例子:求某整数的绝对值:
- //求某整数的绝对值
- int my_abs(int number) {
- if (number < 0) {
- return ~number + 1; //或return -number; 或return ~(n - 1)
- }
- return number;
- }
4、整数二进制表示中最右边的1
获取整数二进制表示中最右侧的1:n & (-n) 等价于 n & ~(n - 1) 。
例如,n = 1100, -n = 0100, 那么n & (-n) = 0100,这样就获取到了二进制表示中最右侧的1。
去除整数二进制表示中最右侧的1:n & (n - 1)
例如,n = 1100,n - 1 = 1011,那么n & (n - 1) = 1000,这样就去除了二进制表示中最右侧的1。
在文章位操作实现加减乘除四则运算中的乘法操作就用到了本小结提到的这两个技巧。
三、位运算相关的题目
1、二进制中1的个数(巧用n&(n-1))
(1) 整数的二进制表示中1的个数 (剑指offer面试题10 统计1的个数)
(2)
输入两个数A和B,输出将A转换为B所需改变的二进制的位数。
首先,A异或B得到的是A和B中不相同位数组成的数,然后再求这个数二进制表示中1的个数,即为所求。
(3) 用 O(1) 时间检测整数 n 是否是 2 的幂次。
如果是2的幂次,应该满足两个条件:1.N>0;2.N的二进制表示中只有一个1;
所以如果使用n & (n - 1)将N中唯一一个1消去,应该返回0;
2、数组中只出现一次的数字(巧用a^b^b=a)
数组中仅出现一次的一个数字、仅出现一次的两个数字
数组中仅出现一次的三个数字