目录
补码
基础知识
按位反
应用举例
按位与
基础知识
应用举例
按位或
基础知识
应用举例
异或运算
基础知识
应用举例
移位运算
基础知识
应用举例
清零取位要用与,某位置一可用或,
若要取反和交换,轻轻松松用异或。
~0得全为1的掩码,不管机器的字大小是多少。
1、特定位清零
//mask的特定位为0,其他位为1
x & mask
2、取出特定位
//mask的特定位为1,其他位为0
x & mask
取出第k位
x >> k & 1
3、判断是偶数还是奇数
x & 1 == 0 偶数
x & 1 == 1 奇数
4、判断一个整数是不是2的幂
boolean power2(int x)
{
return ((x&(x-1))==0)&&(x!=0);
}
5、取模运算转化成位运算 (在不产生溢出的情况下)
a % (2^n) 等价于 a & (2^n - 1)
1、特定位置1
//mask特定位为1,其余位为0
x | mask
2、循环左移和循环右移
//设sizeof(int)=32
//int型变量循环左移k次
a=a<>32-k
//int型变量a循环右移k次
a=a>>k|a<<32-k
*拓展
数字电路中还有同或运算,即两个相同为1,不同为0,此处通过用A o B表示
A o B = ~(A ^ B) = (A & B) | (~A & ~B)
1、特定位取反
若想要第k位取反,mask的第k为1,其余位为0
//s = 6 即1010
//若使第3位取反,则mask = 0010
s ^= mask;
2、不借助其他变量交换两个数
//交换x和y的值
void swap(int &a, int &b)
{
a ^= b;
b ^= a;
a ^= b;
}
题:交换数组中心对称的位置的元素。
//若数组为5 2 7 8 3 结果为3 8 3 2 5
void func(int arr[], size_t size)
{
for (int l = 0, r = size - 1; l < r; ++l, --r)
{
*arr[r] = *arr[l] ^ *arr[r];
*arr[l] = *arr[l] ^ *arr[r];
*arr[r] = *arr[l] ^ *arr[r];
}
}
3、不带进位的模二加(不进位加法),以格雷码为例
二进制码转格雷码
g = b ^ (b>>1)
格雷码转二进制码
4、计算绝对值,利用了模二加
int abs( int x )
{
int y ;
y = x >> 31 ; //得到全0或全1
return (x^y)-y ; //或者 (x+y)^y
}
5、寻找唯一缺少元素或唯一重复元素
题:给定一个由0, 1, 2, ... , n中n个数组成的序列,每个数字至多出现1次,找出唯一缺失的那个数。
解法1:排序再遍历,时间复杂度为O(NlogN)
解法2:使用HashSet存储出现的元素,在查找0~n,时间复杂度为O(N),空间复杂度也为O(N)。
解法3:使用等差数列前[0, n]的n+1项和,减去当前序列的和,时间复杂度为O(N),但是相加可能出现溢出问题。
解法4:使用异或运算的性质,成对出现的数异或为0,借助数组的索引
int missNum(int arr[], size_t size)
{
int res = 0;
res ^= size;
for (int i = 0; i < size; ++i)
{
res ^= i ^ arr[i];
}
return res;
}
*寻找唯一奇数个数的元素解法相同。
C和C++整数分为无符号数和有符号数(默认有符号),Java只有有符号数,java除了左移和右移外,还有无符号右移操作>>>。
C/C++移位操作
左移:对于无符号数,相当于乘2;对于有符号数,左移操作可能改变符号位的值,因此是未定义行为。
右移:对于无符号数,高位补0,相当于除以2,即逻辑右移;对于有符号数,高位补符号位,相当于除以2,即算术右移。
待移位数有w位,移位k位,当k>w时,实际移位为k mod w
1、获得特定位为1的掩码
int mask = (1 << k)
2、获得整数最大值和最小值
int MaxInt = (1 << 31) - 1;
int MinInt = (1 << 31)
3、防止两数取平均加法溢出
C/C++代码
int low, high;
int mid = unsigned(low + high) >> 1
或者
int avg(int x, int y) //返回X,Y 的平均值
{
return (x&y)+((x^y)>>1);
}
java代码
int low, high;
int mid = (low + high) >>> 1
不用移位防止溢出的方法
int low, high;
int mid = low + (high - low) / 2 //在high很大,low为负数也有溢出危险
4、转换运算
乘法运算转化成位运算 (在不产生溢出的情况下)
a * (2^n) 等价于 a<< n
除法运算转化成位运算 (在不产生溢出的情况下)
a / (2^n) 等价于 a>> n
在编译器的优化中,通常会用加减法运算和左移运算组合代替乘常数,用加减法运算和右移运算组合代替除以常数。
如:x*14 转换位(x<<3+(x<<2)+(x<<1)或者(x<<4)-(x<<1)