常见位运算及其经典算法题(1)
位运算顾名思义,是对整数的二进制位直接进行操作的运算。由此很多算法便有了更优解,大大提高了效率。如果你之前没有接触过位运算或者淡忘了,这篇文章或许会对你很有帮助。
1.六大核心运算符
<<(左移运算符)
规则:左移n位,低位补0
例如:5的二进制为0101,5<<2即0101左移两位,变成010100,即20
所以5<<2==20
我们可以发现:根据二进制转十进制的计算,原数字二进制中整体往左移动n位,就是乘以2^n,所以左移运算符的重要用途就是快速计算乘2的幂。
>>(右移运算符)
规则:右移n位,高位补符号位
例如:5>>1,即0101右移两位,等于0010;
(-5)>>1:
5的二进制位0000 0101(原码),求负数的二进制规则就是它对应的正数取反 ,+1,即
取反:1111 1010;+1:1111 1011。即-5=1111 1011(8位)
所以右移一位即:1111 1101,把它转回十进制,即-1,取反
0000 0011等于3,而根据上文表粗的原则,1111 1101=-3,即-5>>1==-3
&(按位与运算符)
规则:两个整数的每一位二进制比较,两个中有0结果就是0,其他都是1
例如:5(0101)& 3(0011)== 1(0001)
同理>>可以计算x//2^n(向下取整)
||(按位或运算符)
规则:两个整数的每一位二进制比较,两个中有1结果就是1,其他都是0
例如:5(0101)|| 3(0011)== 7(0111)
^(按位异或运算符)
规则:相同位0,相反为1,无进位相加
例如:5(0101)^ 3(0011)== 6(0110)
~(按位取反运算符)
规则:0变1,1变0
2.位运算常用结论
- 取出二进制第i位:(x >> i)& 1
- 将第i位改成1:n = n | ( 1 << i )
- 将第i位改成0:n = n & ( ~ ( 1 << i ) )
- 提取二进制中最右侧的1:n & ( -n )
- 将二进制中最右侧的1变成0:n & (n-1)
- 异或运算律:a ^ 0 = a. a ^ a = 0. a ^ b ^ c=a ^ (b ^c)
3.经典例题
这道题我们可以用set数据结构快速解决。但还有一种方法,便是位运算。
题中所给字母均为小写,所以一个符合条件的字符串最多包含a-z 26个英文字母。而整型有32位。我们可以从前往后遍历字符串,得出每个字符关于a的偏移量,将对应位的0变成1.代表此字母已经出现,如果后续再出现就返回false
cpp
class Solution {
public:
bool isUnique(string astr) {
int flag=0;//每一位的0/1便是判断正误的关键
for(auto e:astr)
{
int offset=e-'a';
if(flag&(1<<offset))//判断此位是否已经为1
{
return false;
}
flag=flag|(1<<offset);//将此字母存入flag中
}
return true;
}
};
我们看到这道题后,就能大概率猜出是要用到位运算的知识点.
而这里便用到了异或^无进位相加的性质.
例如2^3==1(0001),异或以后惊奇的发现结果就是无进位相加后的结果,所以我们现在只需算出进位,再将两者异或,知道进位为0便可算出结果.
进位的表达式是(a&b)<<1.例如:

算到这里后,再将0001与0100异或得到0101,他俩的进位为0000,即0101就是结果5
cpp
class Solution {
public:
int getSum(int a, int b) {
while(b!=0)
{
int x=a^b;//^的无进位相加
unsigned int carry=(unsigned int)(a&b)<<1;//记录进位
a=x;
b=carry;
}
return a;
}
};
今天就是这些东西。如有疑问,欢迎打在评论区。
主页还有更多优质内容OvO
