13.算法——位运算

运算符号

位运算主要运用二进制运算的特性进行优化,主要运用运算符号包括

  1. 异或:^, x ^ x=0, x ^ 0=x, x ^ 全1= ~x
  2. 与 : & , x & x=x,x & 0=0,x & 1=x
  3. 或:|,x | x=x,x |0=0,x | 1=x;
  4. 取反:~
  5. 左移:<<;移动一位:x<<=1;
  6. 右移:>>

注意:优先级高于一次运算符,所以如果写。。。。。注意左边要打括号。

常用操作

n&(n-1) 去除最低位的1

n&(n-1) 可以去除最低位的1.
假设有 n=1011 0101,则n-1=1011 0100, n&(n-1)=1011000 .等于把n最低位的1抹去。
假设有n=1101100,则n-1=1101011 ,n&(n-1)=1101000.
我们发现,原理很简单,因为n的最低位1必定存在(除0以外),那么n-1的表示必定是该位为0,该位后面所有位为1.
那么,在该位由于1 &0 =0,该位得0
该位后面,同理0&1=0,由此抹去最低位的1.

n&(-n) 得到最低位1

n&(-n)可以得到最低位1
假设有 n=0 1011 0101,那么(-n)怎么表示,我们知道负数在计算机中以补码形式存储。-n原码为1 1011 0101,反码 -n=1 0100 1010,补码-n=1 0100 1011.所以 n=1011 0101 取负得到 0100 1011.
n&(-n)=0000 0001,得到最低位1.
假设有n=01111 0100,-n原=11111 0100 ,-n反=10000 1011 ,-n补=10000 1100.所以n=1111 0100取负得到 0000 1100
n&(-n)=0000 0100得到最低位1.
原理很简单,这是因为所有数再取负之后,除了最低位1之外的所有位都1-0变换了,由此我们得到最低位的1.

231. 2 的幂

在这里插入图片描述
我们从二进制表示入手发现:
次方 n n-1
1: 0000 0001 0000 0000
2: 0000 0010 0000 0001
二的幂次方n只有最低位是1其余全0,而n-1该位为0,后面所有位为1.由此n&(n-1)=0必定成立,我们要注意n必须大于零

class Solution {
public:
    bool isPowerOfTwo(int n) 
    {
        return n>0 && (n&(n-1))==0;
    }
};

342. 4的幂

我们知道如果是4的幂一定能推出其是2的幂,但是2的幂不一定是4的幂,这个很好理解,比如2 ,8,32.明显就不是4的幂。
我们将n开根号,得到一个整数x,这里如果其是4的幂应该能刚好开到整数,所以如果x<<=1如果!=n显然不是4的幂,接下来这个x只要满足2 的幂即可。

class Solution {
public:
    bool isPowerOfFour(int n) 
    {
        if(n<=0) return false;
        int x=sqrt(n);
        
        if ((x&(x-1))!=0 ) return false;
        
        return x*x==n;  
        

    }
};

461. 汉明距离

在这里插入图片描述

class Solution {
public:
    int hammingDistance(int x, int y) 
    {
        int z=x ^ y,ans=0;//异或,统计有多少1即可
        while(z)
        {
            //看z的最后一位是否是1
            ans+=z&1;
            z>>=1;
        }
        return ans;

    }
};

190.颠倒二进制位

在这里插入图片描述
每次取n的最后一位,将其添到ans末尾。由此我们从右到左遍历的n,从左到右得到的ans。即可颠倒n
细节问题:每次要先把ans左移,这样加上来n的最后一位不是0就是1,就可以实现添加到ans末尾的操作。结束之后将n右移进入下一次循环。

class Solution {
public:
    uint32_t reverseBits(uint32_t n) 
    {
        uint32_t ans=0;
        for(int i=0;i<32;++i)
        {
            ans<<=1;
            ans+=n&1;
            n>>=1;
        }
        return ans;
        
    }
};

136. 只出现一次的数字

在这里插入图片描述
显然题目要求不使用额外空间,那么像哈希集等方法需要o(n)的空间复杂度。不符合题意。
我们考虑使用位运算,我们来看看异或的性质

  1. x^0=x;
  2. x^x=0;
  3. x ^ ( y^ z)= (x^y) ^z;

题目条件

  1. 只有一个 数只出现一次
  2. 其余所有数都出现了两次。

那么我们异或所有数,有重复的两个数异或完就变成0了,最终剩下来的数必定是单独的x。

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int x=0;
        for(auto i:nums)
        {
            x^=i;
        }
        return x;

    }
};

318. 最大单词长度乘积

在这里插入图片描述
我们第一反应是用hashmap来存储每个单词的字母出现情况,其实这里用位运算可以很巧妙的记录字母出现情况。
因为只有26个小写字母,所以我们可以用一个int型的32位整数来记录每个字母的出现。
从最右边为a,依次往左。
对于每个单词,我们初始一个x=0,然后每次拿1往左移动ch-'a’位。然后用x与其&。
比如abc这个单词我们在记录里就是:000…111
然后我们就只需要一个数组就可以记录每个单词出现了哪些字母
题目要求不能有相同字母,我们将数组中两个数相&,得到0即表示没有相同的数。此时更新最大长度即可。

class Solution {
public:
    int maxProduct(vector<string>& words) 
    {
        int n=words.size();
        vector<int> flag(n);
        int i=0,maxlength=0;;
        for(auto word:words)
        {
            int x=0;
            for(char ch:word)
            {
                x|=1<<(ch-'a');
            }
            flag[i++]=x;

        }
        for(int i=0;i<words.size()-1;++i)
        {
            for(int j=i+1;j<n;++j)
            {
                if((flag[i]&flag[j])==0)
                {
                    int nl=words[i].size()*words[j].size();
                    maxlength=max(maxlength,nl);
                }
            }
        }
        return maxlength;

    }
};

338. 比特位计数

在这里插入图片描述
解法1:我们遍历每个数,对每个数进行数1操作。
有如下方法可以数1:
解法1.1: 每次取最后一位&1,ans+=刚才的值;(慢)
解法1.2: 利用n&(n-1)去除最低位1的性质,每次去除最低位1,++ans,直到这个数变成0;(较快)

解法2:我们写几个数看一下

  1. 0001 一个
  2. 0010 一个
  3. 0011 两个
  4. 0100 一个
  5. 0101两个
  6. 0110 两个

第一:很显然只要是奇数,(即二进制的最后一位是1),必定比上一个多一个
当是偶数的时候呢?我们注意观察,2和1一样,4和2一样,而6又和3一样。所以偶数的时候和他一半一样多。

dp[i]= i&1? dp[i-1]+1:dp[i>>1];

所以动态规划很快就出来了

class Solution {
public:
    vector<int> countBits(int n) 
    {
        vector<int> ans(n+1,0);
        for(int i=1;i<=n;++i)
        {
            ans[i]= i&1? ans[i-1]+1:ans[i>>1];//如果i末尾是1,说明上个数末尾是0,当前1的个数比上个多1
        }
        return ans;

    }
    
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值