位运算(10题)

目录

一、基础知识

1.基础位运算

2.给一个数n,确定它的二进制表示中的第x位是0还是1

3.将一个数n的二进制表示的第x位修改成1

4.将一个数n的二进制表示的第n位修改成0

5.位图的思想

6.提取一个数n,二进制表示中最右侧的1

7.将一个数n二进制表示中最右侧的1抹去

8.位运算的优先级

9.异或 ^,运算的运算律

二、基础题目示例

1.位1的个数

2.比特位计数

3.汉明距离

4.只出现一次的数字

5.只出现一次的数字3

三、题目演示

1.判断字符是否唯一

2.丢失的数字

3.两整数之和

4.只出现一次的数字2

5.消失的两个数字


一、基础知识

1.基础位运算

        <<  左移操作符                     &按位与     有0就是0   

        >>右移操作符                       |  按位或       有1就是1             

        ~按位取反                           ^异或            相同为1,相异为0 / 无进位相加

                                                    例如     1 0 1 1 0 1   ^

                                                                 1 1 0 1 1 0    

                                                     结果为 0 1 1 0 1 1,无进位相加就是1 + 0 = 0,1+1 = 0,但是不会向高位进位

2.给一个数n,确定它的二进制表示中的第x位是0还是1

我们规定从右往左,的位数为第0,1,2,3,4,5.....31位

这样右移动的距离等于它的位数

(n >> x) & 1   ->  0 ------ 0

                           1 ------ 1

3.将一个数n的二进制表示的第x位修改成1

                                                                n = n | (1 << x)

4.将一个数n的二进制表示的第n位修改成0

                                                        n = n &(~(1 << x))

5.位图的思想

                                                  每个比特位0或者1的数值表示一种状态

6.提取一个数n,二进制表示中最右侧的1

                                                lowbit = n & -n(取反加一即变为负)

7.将一个数n二进制表示中最右侧的1抹去

                                        n = n & (n - 1)

8.位运算的优先级

        能加括号就加括号

9.异或 ^,运算的运算律

1.a ^ a = 0

2.a ^ 0 = a

3.a ^ b ^ c = a ^ (b ^ c)

二、基础题目示例

1.位1的个数

. - 力扣(LeetCode)

class Solution {
public:
    int hammingWeight(int i) {
        int ret = 0;
        int n = 32;
        while(n > 0)
        {
            if((i & 1) == 1)ret++;
            i  = i >> 1;
            n--;
        }
        return ret;
    }
};

        将要判断的数每次按位与1,若为1则此位为1,然后再右移一位

2.比特位计数

. - 力扣(LeetCode)

class Solution {
public:
    int Add(int i)
    {
        int ret = 0;
        int n = 32;
        while(n > 0)
        {
            if((i & 1) == 1)ret++;
            i  = i >> 1;
            n--;
        }
        return ret;
    }
    vector<int> countBits(int n) {
        vector<int>ret(n+1,0);
        for(int i = 0; i <= n;i++)
        {
            ret[i] = Add(i);
        }
        return ret;
    }
};

        原理同上,只是多了一个循环

3.汉明距离

. - 力扣(LeetCode)

class Solution {
public:
    int hammingDistance(int x, int y) {
        int n = 32;
        int ret = 0;
        while(n>0)
        {
            if((x & 1) != (y & 1))ret++;
            x = x >> 1;
            y = y >> 1;
            n--;
        }
        return ret;
    }
};

        分别用 &1 来判断两个数最低位是0还是1,对两个数进行比较,然后分别不断右移这两个数

4.只出现一次的数字

. - 力扣(LeetCode)

        利用异或的性质即可

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

5.只出现一次的数字3

. - 力扣(LeetCode)

class Solution {
public:
    vector<int> singleNumber(vector<int>& nums) {
        long long n = 0;
        for(auto ch: nums)
        {
            n ^= ch;
        }
        long long mask = n &(-n);
        mask = (int)mask;
        vector<int>ret(2,0);
        for(auto ch: nums)
        {
            if((ch & mask) == mask)ret[0] ^= ch;
            else ret[1] ^= ch;
        }
        return ret;
    }
};

        我们先将所有值异或,这个过程会将相同的数都消去,最后得到两个不同的数a,b异或的结果。

        这个数为1的位置,是两个数上值不同的位置。为了方便,我们取出最右侧的1。

        a,b两个数中,一个数的该位置为1,一个数为0.

        而待分开的数的该位置也一定为1或0.因此我们将全部数分为两组

        a,xx  yy zz                                b,mm,nn,oo

        这样即可分别取出单独的数

三、题目演示

1.判断字符是否唯一

. - 力扣(LeetCode)

class Solution {
public:
    bool isUnique(string astr) {
        int size = astr.size();
        if(size>26)return false;
        int bitmap = 0;
        for(int i = 0; i < size; i++)
        {
            int distance = astr[i]-'a';
            if(((bitmap >> distance) & 1) == 1)return false;
            else
            {
                bitmap = bitmap | (1 << distance);
            }
        }
        return true;
    }
};

        这里我们不使用数据结构,我们使用位图来储存信息,达到减小空间复杂度的效果。每个比特位储存一个字符的存在情况,‘a’的位置是右侧第一位,我们记为0下标。

        首先我们记录字符串的长度,来进行遍历操作,如果字符串比26长,那么必定有重复

每次进入循环的时候,我们查看这个字符对应位置是否为1,如果是,我们返回fasle,如果不是,我们将位图的此位改为1.

2.丢失的数字

. - 力扣(LeetCode)

class Solution {
public:
    int missingNumber(vector<int>& nums) {
        int size = nums.size();
        int ret = 0;
        for(int i = 0; i < size; i++)
        {
            ret = ret ^(nums[i]);
        }
        for(int i = 0; i <= size; i++)
        {
            ret = ret ^ i;
        }
        return ret;
    }
};

        利用异或的性质,例如

        我们选取0-5中的5个数字

        1 5 2 3 0

        0 1 2 3 4 5

        我们用0分别遍历异或上下两组数字,上下两组中相同的数字进过异或都变为0,最后只剩下0-5中剩余的那个数字,也即丢失的数字, 0 ^ 这个数字,仍然为这个数字

3.两整数之和

. - 力扣(LeetCode)

class Solution {
public:
    int getSum(int a, int b) {
        while(a & b)
        {
            int tmp = a;
            a = a ^ b;
            b = (tmp & b) << 1;
        }
        return a ^ b;
    }
};

        我们首先要知道,异或运算是一种无进位相加,那我们如何做到有进位呢?只需要把a按位与b即可,这样我们可以知道哪些位置是要进位的,然后再左移一位,相当于进位操作。

         但是进位之后相加还可能再次进位,因此我们循环这个过程,我们不断用a^b,a&b更新a和b的值,直到a&b为0。此时无进位。因为任意数异或0等于它本身,因此我们循环结束统一返回a^b即可

4.只出现一次的数字2

. - 力扣(LeetCode)

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int size = nums.size();
        int ret = 0;
        for(int i = 0; i < 32; i++)
        {
            int sum = 0;
            for(auto ch:nums)
            {
                sum += (ch >> i)&1;
            }
            sum = sum % 3;
            if(sum)ret = ret | (sum << i);
        }
        return ret;
    }
}; 

 

        原理如上,可以推广,任意重复n次就对n取余 

5.消失的两个数字

. - 力扣(LeetCode)

class Solution {
public:
    vector<int> missingTwo(vector<int>& nums) {
        int size = nums.size()+2;
        int n = 0;
        for(int i = 1; i <= size; i++)
        {
            n ^= i;
        }
        for(auto ch:nums)
        {
            n ^= ch;
        }
        long long mask = n &(-n);
        vector<int>ret(2);
        for(int i = 1; i <= size; i++)
        {
           if((i & mask) == mask)ret[0] ^= i;
            else ret[1] ^= i;
        } 
        for(auto ch:nums)
        {
            if((ch & mask) == mask)ret[0] ^= ch;
            else ret[1] ^= ch;
        }
        return ret;
    }
};

        与基础题目示例中的5.只出现一次的数字3相似,只是遍历范围是一个数组加上一串连续的数字

  • 9
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值