常见位运算总结
1、基础位运算
&(按位与):有0就是0
|(按位或):有1就是1
^(按位异或):相同为0,相异为1或者无进位相加
2、给定一个数n,确定二进制第x位是1还是0
(n>>x)&1
3、将一个数n的二进制第x位修改为1
n|=1
4、将一个数n的二进制第x为修改为0
n&=(~(1<<x))
5、提取一个数n二进制最右边的1
注:-n=~n+1
n&-n
-n将n先取反,然后+1
6、删除一个数n二进制最右边的1
n&(n-1)
-1就可以把最右边的1借位过去从而变成0,该位置后面全是0,现在借位过去就会全变成1
1、判定字符是否唯一
https://leetcode.cn/problems/is-unique-lcci/description/
算法原理:
只有26个字母,所以只需要位图26位比特位就能存
按位或将字母存到对应位置,比如a就存在位图第一位,b在第二位
通过按位与操作判断当前位置
代码
cpp
class Solution {
public:
bool isUnique(string astr) {
// 位图
// 只有26个字母,把astr进行标记
int str = 0;
for (auto s : astr) {
int now =1<<(s - 'a');
if (now & str) // 当前字母已经存在
{
return false;
}
str |= now;
}
return true;
}
};
2、丢失的数字
算法原理
异或运算:相同数字进行异或运算,结果是0
代码
cpp
class Solution {
public:
int missingNumber(vector<int>& nums) {
int ret=0;
int size=nums.size();
for(int i=0;i<size;i++)
{
ret^=i^nums[i];
}
return ret^size;
}
};
3、两整数之和
算法原理:
1、a^b 是 无进位和的位置
2、(a&b)<<1 是 进位的位置
一直循环 上述操作,直到最后(a&b)<<1为0,也就是没有进位了,那么a^b就是最后结果
画图理解
cpp
13+28=41
32 16 8 4 2 1
a+b
a: 0 0 1 1 0 1
b: 0 1 1 1 0 0
------------------------------------------
a^b: 0 1 0 0 0 1 --(a)
(a&b)<<1: 0 1 1 0 0 0 --(b)
------------------------------------------
a^b: 0 0 1 0 0 1 --(a)
(a&b)<<1: 1 0 0 0 0 0 --(b)
------------------------------------------
a^b: 1 0 1 0 0 1 ☆ →41
(a&b)<<1: 0 0 0 0 0 0
代码
cpp
class Solution {
public:
int getSum(int a, int b) {
//a^b 不进位相加
//(a&b)<<1 进位的位置
if(b==0)
return a;
while(b!=0)
{
int tmpa=a^b;
int tmpb=(a&b)<<1;
a=tmpa;
b=tmpb;
}
return a;
}
};
4、只出现一次的数字 II
137. 只出现一次的数字 II - 力扣(LeetCode)
算法原理:
把所有数的二进制位分开相加,因为有三个相同的数,也就是每位上都有3n个0或者1,再加上出现一次的1或者0,会出现如下规律
cpp
(3n个0 +0 )%3 =0
(3n个1 +0 )%3 =0
(3n个0 +1 )%3 =1
(3n个1 +1 )%3 =1
进行%3操作之后,只有出现一次的数在那个位上为1最后的结果才会为1
代码
cpp
class Solution {
public:
int singleNumber(vector<int>& nums) {
//最大数为32位,把所有数二进制位分开相加,然后%3,多出来的位置一定是1
int ret=0;
for(int i=0;i<32;i++)
{
int count=0;
for(auto e: nums)
{
//将所有数进行按位与运算
count+=(e>>i)&1;
}
ret|=(count%3)<<i;
}
return ret;
}
};
5、只出现一次的数字 III
260. 只出现一次的数字 III - 力扣(LeetCode)
算法原理:
1、所有数进行异或操作,最后异或的结果就是两个不重复的数的异或结果x
2、通过x&(-x)取最后一个1的位置
为什么要取这个位置?
异或相同为0,相异为1,最后一个位置上为1,表示一个数当前位置为0,另一个当前位置为1
3、通过判断所有元素在x最后一个1的位置是否为1
是1的异或到type1
不是1的异或到type2
为什么这样就可以将两个不重复数分开?
因为相同数在这个位置要么都在type1,要么都在type2,都会异或为0
代码
cpp
class Solution {
public:
vector<int> singleNumber(vector<int>& nums) {
int xorsum=0;
for(auto num:nums)
{
xorsum^=num;
}
//INT_MIN取反会溢出
int last=(xorsum==INT_MIN?xorsum:xorsum&(-xorsum));//取出二进制最低位的1
int type1=0,type2=0;//将nums中那个位置上为0放在type1中,为1的放在type2中
for(auto num:nums)//两个元素不同,所以那个位置上面一个是0,一个是1,这样异或之后最终type1,type2就是两个不同的数
{
if(num&last)
{
type1^=num;
}
else
{
type2^=num;
}
}
return {type1,type2};
}
};
6、消失的两个数字
面试题 17.19. 消失的两个数字 - 力扣(LeetCode)
算法原理:
第三题丢失的数字+第五题只出现一次的数字 |||
1、相同数字异或为0,找到两个小时数字异或之后的结果
2、然后使用第五题的算法原理,就可以将两个数分开
代码
cpp
class Solution {
public:
vector<int> missingTwo(vector<int>& nums) {
//找到最后一位
int sum=0;
int size=nums.size();
for(int i=0;i<size;i++)
{
sum^=i^nums[i];
}
sum^=size;
sum^=size+1;
sum^=size+2;
int last=sum&(-sum);
int type1=0,type2=0;
//将nums中的数分别存放到type1,type2
//后面相同异或为0
for(auto e:nums)
{
if(e&last)
type1^=e;
else
type2^=e;
}
for(int i=1;i<=size+2;i++)
{
if(i&last)
type1^=i;
else
type2^=i;
}
return {type1,type2};
}
};