创作不易,感谢三连支持!!
一、常见的位运算总结
标题
二、位1的个数
利用第七条特性:n&(n-1)干掉最后一个1,然后每次都用count++去统计,直到变成0
class Solution { public: int hammingWeight(uint32_t n) { int count=0; while(n) { n&=(n-1); ++count; } return count; } };
三、汉明距离
利用异或相同为0相异为1的特点,x和y异或后不一样的位都会变成1,这个时候再用n&(n-1)去统计1个个数,即为这两个数字的汉明距离
class Solution { public: int hammingDistance(int x, int y) { //异或的特点,相同为0,相异为1 然后再利用n&(n-1)统计1的个数 int n=x^y; int count=0; while(n) { n&=(n-1); ++count; } return count; } };
四、比特数记位(典型dp)
思路1:每遍历一个数都用n&(n-1)去数他一共有多少个1,然后放心ans数组中
class Solution { public: int countOnes(int x) { int ones=0; while(x) { x&=(x-1); ++ones; } return ones; } //利用 vector<int> countBits(int n) { vector<int> ret(n+1); for(int i=1;i<=n;++i) ret[i]=countOnes(i); return ret; } };
思路2:动态规划思想(本质是根据位运算的性质通过已经计算出来的状态去求未计算的状态)
即当计算 i 的「一比特数」时,如果存在 0≤j<i,j 的「一比特数」已知,且 i和 j 相比,i 的二进制表示只比j多了一个 1,则可以快速得到 i 的「一比特数」。(利用位运算的性质)
1、设置最低位
对于n(n-1),本质上是将最右侧的1干掉,所以一定会比原来的n小!!
因此bit[i]=bit[i&(i-1)]+1 恒成立!!
class Solution { public: vector<int> countBits(int n) { vector<int> ret(n+1); for(int i=1;i<=n;++i) ret[i]=ret[i&(i-1)]+1; return ret; } };
2、最低有效位
对于正整数x来说,如果是偶数的话,右移一位正好就是x/2,并且1的个数不会变,所以偶数bit[i]=bit[i/2] 对于奇数来说,右移一位会丢掉后面的1,所以要给他补上去,所以奇数等于bit[i]=bit[i/2]+1 为了修正奇数多出来的1,我们可以利用i&1,如果是奇数就是1,偶数就是0,因此 bit[i]=bit[i/2]+(i&1) 恒成立!!
class Solution { public: vector<int> countBits(int n) { vector<int> ret(n+1); for(int i=1;i<=n;++i) ret[i]=ret[i/2]+(i&1); return ret; } };
3、最高有效位
对于正整数 x,如果可以知道最大的正整数 y,使得 y≤x,y 是 2的整数次幂,则 y的二进制表示中只有最高位是 1,其余都是 0,此时称 y 为 x 的「最高有效位」。令 z=x−y,显然 0≤z<x,则 bits[x]=bits[z]+1,所以我们使用heightbit作为当前的最高有效位。如果i&(i-1)为最高有效位,就更新heightbit,i 比 i−highBit的「一比特数」多 1 所以bit[i]=bit[i-height]+1 恒成立!!
class Solution { public: vector<int> countBits(int n) { vector<int> ret(n+1); int heightbit=0; for(int i=1;i<=n;++i) { if((i&(i-1))==0) heightbit=i; ret[i]=ret[i-heightbit]+1; } return ret; } };
五、只出现一次的数字(1)
思路:利用异或的性质,出现两次的数异或后为0,出现一次的数异或0还是本身
class Solution { public: int singleNumber(vector<int>& nums) { int n=0; for(auto &e:nums) n^=e; return n; } };
六、只出现一次的数字(2)
class Solution { public: int singleNumber(vector<int>& nums) { int ret=0; for(int i=0;i<32;++i) { int sum=0; for(int num:nums) if((num>>i)&1) ++sum;//统计个数每一个1的比特位 sum%=3;//模3后代表ret当前bit位的值 if(sum==1) ret|=(1<<i);//sum==1就让ret该位置变成1 } return ret; } };
七、只出现一次的数字(3)
class Solution { public: vector<int> singleNumber(vector<int>& nums) { unsigned int temp=0;//遇到INT_MIN会溢出,10000……00 for(int num:nums) temp^=num; int x=temp&(-temp);//x拿到最后一个1 //int x= (temp == temp ? temp : temp & (-temp)); int type1=0,type2=0; for(int num:nums) if(num&x) type1^=num; else type2^=num; return {type1,type2}; } };
八、丢失的数字
思路1:利用等差数列和的公式-数组每一位数相加,即可得到消失的数
class Solution { public: int missingNumber(vector<int>& nums) { int n=nums.size(); return n*(n+1)/2-accumulate(nums.begin(),nums.end(),0); } };
思路2:利用异或的特点,让0和数组所有数进行异或,再对1-n异或,出现两次的数都会被消去,最后只会留下答案
class Solution { public: int missingNumber(vector<int>& nums) { int ret=0; for(int num:nums) ret^=num; for(int i=0;i<=nums.size();++i) ret^=i; return ret; } };
思路3:暴力快排+寻找下标和数字对应不上的数
class Solution { public: int missingNumber(vector<int>& nums) { sort(nums.begin(),nums.end()); int n=nums.size(); for(int i=0;i<n;++i) if(nums[i]!=i) return i; return n; } };
九、消失的两个数字
该题就是只出现一次数组(1)和 (3)的结合,所以以上的思路去解答即可
class Solution { public: vector<int> missingTwo(vector<int>& nums) { int temp=0; for(auto e:nums) temp^=e; for(int i=1;i<=nums.size()+2;++i) temp^=i; int x=temp&(-temp);//拿到最右边的1 int num1=0,num2=0; for(auto e:nums) if(e&x) num1^=e; else num2^=e; for(int i=1;i<=nums.size()+2;++i) if(i&x) num1^=i; else num2^=i; return {num1,num2}; } };
十、判定字符是否唯一
class Solution { public: bool isUnique(string astr) { if(astr.size()>26) return false;//鸽巢原理做优化 //利用位图的思想 int bitmap=0; for(auto ch:astr) { int i=ch-'a';//找到映射关系 if((bitmap>>i)&1==1) return false;//先判断该字符是否出现过 判断第i位是否是1 //如果没出现过,将第i位的0修改为1 bitmap|=(1<<i); } return true; } };
十一、或运算的最小翻转次数
class Solution { public: int minFlips(int a, int b, int c) { int ret=0;//记录翻转次数 for(int i=0;i<31;++i) { //找到每一位进行判断 int bit_a=(a>>i)&1; int bit_b=(b>>i)&1; int bit_c=(c>>i)&1; //情况1,如果c为0的话,那么a和b必须全是0 所以他们是多少就要翻转几次 if(bit_c==0) ret+=(bit_a+bit_b); //情况2,如果c为1的话,那么a和b至少要有一个为1 如果都为0,翻转一次,如果有1就不用翻转 else ret+=(bit_a+bit_b==0); } return ret; } };
十二、两整数之和
class Solution { public: int getSum(int a, int b) { //要考虑-1,因为-1的右移操作是没有定义的 while(b) { int x=a^b; int carry=(a&b)<<1; a=x; b=carry; } return a; } };