位运算技巧总结
基本运算性质
自身运算
a & a = a
a | a = a
a ^ a = 0
与0运算
a & 0 = 0
a | 0 = a
a ^ 0 = a
与全1运算
a & (~0) = a
a | (~0) = ~0
a ^ (~0) = ~a
与或异或运算率
交换律
a | b = b | a
a & b = b & a
a ^ b = b ^ a
结合律
单个运算符内部满足结合率
(a | b) | c = a | (b | c)
(a & b) & c = a & (b & c)
(a ^ b) ^ c = a ^ (b ^ c)
运算符交叉不满足结合律。
例如(a & b) | c ≠ a & (b | c)
分配律
与运算和或运算满足分配律
(a & b) | c = (a | c) & (b | c)
(a | b) & c = (a & c) | (b & c)
有异或运算的部分不满足
(a ^ b) & c = (a & c) ^ (b & c)
(a & b) ^ c ≠ (a ^ c) & (b ^ c)
(a | b) ^ c ≠ (a ^ c) | (b ^ c)
(a ^ b) | c ≠ (a | c) ^ (b | c)
简单的求证代码
#include <stdio.h>
#include <windows.h>
#include<iostream>
using namespace std;
int main()
{
std::cout << std::boolalpha;
for(int i=0;i<100;i++){
int a=rand(),b=rand(),c=rand();
int cal1=(a^b)|c;
//int cal2=a&(b|c);
int cal2=(a|c)^(b|c);
cout<<"a:"<<a<<" "<<"b:"<<b<<" "<<"c:"<<c<<" "<<endl<<"cal1:"<<cal1<<" "<<"cal2:"<<cal2<<" "<<"euqal:"<<(cal1==cal2)<<endl;
}
//cout<<((1<<31)-1)<<endl<<(1<<31)<<endl;
system("pause");
return 0;
}
优先级
位运算优先级比较低,所以一定要加括号
技巧
异或取反
//全1异或取反
a ^ (~0) = ~a
//i+1位取反
a ^ (1<<i)
取int极大极小值
1、int最大值 (1<<31)-1
2、int最小值 1<<31
取负
-a = ~a + 1
原地交换操作
a ^= b;
b ^= a;
a ^= b;
判断奇偶
a & 1
比较
a^b==0
判断符号是否相同
a ^ b >= 0
第i+1位操作
//取出第i+1位
a & (1<<i)
//i+1位 置1
a |=1<<i
//i+1位 置0
a &=~(1<<i)
//i+1位 反转
a ^ (1<<i)
异或去重
a ^ a = 0
//出现偶数次的即可消去
异或可逆
leetcode 1720
//推导
a^b=c ----> a=c^b b=c^a
a^b^b=c^b
a^0=c^b
a=c^b
删除最末尾的1个1,用于统计1的个数
leetcode 191
a & (a-1)
仅保留最后一个1
a & (-a)