位运算题目解析


前言

位运算一共分为两大类,逻辑运算符和位移运算符


2的幂

给你一个整数 n,请你判断该整数是否是 2 的幂次方。如果是,返回 true ;否则,返回 false 。
如果存在一个整数 x 使得 n == 2的x次方 ,则认为 n 是 2 的幂次方。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/power-of-two
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
我们知道,10进制在化为2进制的时候,数字以0和1构成,比如说2的幂最高位为1其余全部是0,当2的幂减一的时候,我们可以知道,与原来没减一的值相与为0
所以我们可以用一行代码直接秒杀

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

我们可以看到,过啦!
在这里插入图片描述

4的幂

给定一个整数,写一个函数来判断它是否是 4 的幂次方。如果是,返回 true ;否则,返回 false 。
整数 n 是 4 的幂次方需满足:存在整数 x 使得 n == 4的x次方
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/power-of-four
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

关于四的幂我们来思考一下,4的幂一定是2的幂,但是2的幂不一定是4的幂,利用数学归纳法2^(2x)%3 =1
2^(2x+1)%3=2,,所以4的幂模3一定等于1,所以4的幂满足2的幂模3等于1,我们来实现一下

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

我们来看,过啦!
在这里插入图片描述

位1的个数

编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。
提示:
请注意,在某些语言(如 Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。
在 Java 中,编译器使用二进制补码记法来表示有符号整数。因此,在上面的 示例 3 中,输入表示有符号整数 -3。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/number-of-1-bits
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

我们来分析一下,比如说1000,减去1以后,就变成了111,与原数相与则得出值为0,再比如100100,减一以后就是100011,相与后,我们也把最低位的1也消掉了,所以思路就是写一个循环,每次循环就是一个数与其减一后的数相与,直到这个数变成0以后,期间把相与的次数记录下来。

class Solution {
public:
    int hammingWeight(uint32_t n) {
        int count =0;
        while(n){
            n=n&(n-1);
            count++;
        }
        return count;
    }
};

然后我们看到,过啦!
在这里插入图片描述

交换数字

编写一个函数,不用临时变量,直接交换numbers = [a, b]中a与b的值。
示例:
输入: numbers = [1,2]
输出: [2,1]
提示:
numbers.length == 2
-2147483647 <= numbers[i] <= 2147483647
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/swap-numbers-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

这个题目用中间变量就能解决,如果不用中间变量,我们也用位运算去解决
我们可以用异或操作去交换两个数的值,直接给出算法,a与b交换位置
a=a^b
b=a^b
a=a^b
就像这样,我们来分析前两行,a=a^b以后
到了第二行,b=(a^b) ^ b,异或操作可以用结合律,b = a ^ (b ^ b)=a ^ 0 = a,就实现了a与b的转换
再到第三行,a = (a ^ b) ^ ((a ^ b) ^ b) = b ,也实现了交换
则我们来写代码

class Solution {
public:
    vector<int> swapNumbers(vector<int>& numbers) {
        numbers[0]=numbers[0]^numbers[1];
        numbers[1]=numbers[0]^numbers[1];
        numbers[0]=numbers[0]^numbers[1];
        return numbers;
    }
};

我们来看一下,过啦!
在这里插入图片描述

只出现一次的数字

给你一个整数数组 nums ,除某个元素仅出现 一次外,其余每个元素都恰出现两次 。请你找出并返回那个只出现了一次的元素。
这也可以使用异或,因为除了那个只出现一次的数字其余的都出现了两次,连续异或后只会存在出现一次的那个数

class Solution {
public:
    char firstUniqChar(string s) {
        unordered_map<char,int>hash;
        for(auto c:s)
        hash[c]++;
        char res=' ';
        for(auto c:s)
        {
            if(hash[c]==1)
            {
                res=c;
                break;
            }
        }
        return res;
    }
};

我们可以看到,过啦!
在这里插入图片描述

汉明距离
两个整数之间的 汉明距离 指的是这两个数字对应二进制位不同的位置的数目。
给你两个整数 x 和 y,计算并返回它们之间的汉明距离。

我们分析一下,根据异或的性质,两个数异或以后的二进制位上遗留的1的个数就是汉明距离
所以这就被转化为了求1的个数,这个题我们是做过滴。

class Solution {
public:
    int hammingDistance(int x, int y) {
        int n=x^y;//异或后求二进制中1的个数
        int count=0;
        while(n){
            n&=(n-1);
            count++;
        }
        return count;
    }
};

我们可以看到,过啦!
在这里插入图片描述

交替二进制数

给定一个正整数,检查它的二进制表示是否总是 0、1 交替出现:换句话说,就是二进制表示中相邻两位的数字永不相同。
考查的就是将10进制转化为2进制,相邻两位是否存在相同的数字,则可以用3去位与每两个位,看是否为10或者01,或者相与之后看结果是否是3或者0,然后再往右移动一位,然后直到n等于0

class Solution {
public:
    bool hasAlternatingBits(int n) {
       while (n) {
           if ((n&3) ==3 || (n&3) == 0) {
               return false;
           }
           n=n>>1;
       }
       return true;
    }
};

我们可以看到,过啦
在这里插入图片描述

找出所有子集的异或总和再求和

一个数组的 异或总和 定义为数组中所有元素按位 XOR 的结果;如果数组为 空 ,则异或总和为 0 。
例如,数组 [2,5,6] 的 异或总和 为 2 XOR 5 XOR 6 = 1 。
给你一个数组 nums ,请你求出 nums 中每个 子集 的 异或总和 ,计算并返回这些值相加之 和 。
注意:在本题中,元素 相同 的不同子集应 多次 计数。
数组 a 是数组 b 的一个 子集 的前提条件是:从 b 删除几个(也可能不删除)元素能够得到 a 。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sum-of-all-subset-xor-totals
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

这个题呢比较复杂,主要在于判断数组中的这个值是否存在集合中,首先一个数组的子集加上空集的话一共是2的数组长度次方个,比如说数组中元素的个数为n,则子集的个数为2^n个,则个数也可以表示成1<<n,第一个外层循环遍历集合可以用这样表示for(int i=0;i<(1<<n);i++),好的外层循环遍历子集我们完成了,那么我们怎么去实现查找数组中的元素是否在集合中呢,我们的集合通过外层循环的方式,也按照顺序排列了。主要是用!(i&(1<<j))这个去判断,表示i用2进制表示后的第j位是否为1,也就是i这个集合中,是否存在第j个元素,把数组中的第j个元素拿出来进行异或操作,然后把得到的结果进行累加。比如说,有一个数组{1,2,3,4},i从0000到1111进行遍历,有一个子集是{2,4}则i表示为0101,那么j对应的应该就是第2个元素和第4个元素符合if当中的条件,j的下标是1和3进行异或操作

class Solution {
public:
    int subsetXORSum(vector<int>& nums) {
        int i,j,ans;
        int sum=0;
        for(i=0;i<(1<<nums.size());++i){
            ans = 0;
            for(j=0;j<nums.size();++j) {
                if(!(i&(1<<j))){
                    ans ^= nums[j];
                }
            }
            sum += ans;
        }
        return sum;
    }
};

在这里插入图片描述

两整数之和

给你两个整数 a 和 b ,不使用 运算符 + 和 - ​​​​​​​,计算并返回两整数之和。
位运算中的异或,对应位相同为0,不同为1,就相当于每一位的相加操作,相当于不带进位的加法,但是我们需要考虑进位的因素,所以我们必须想到进位的存储。
我们就把问题转换成了,不带进位的相加,以及带进位加和的值
a ^ b 与(a&b)<<1的和,我们就可以考虑用递归的方式去处理,我们就可以考虑到递归的出口就是a或者b有一个值为0,作为递归的出口getSum(a,b),所以直接拿a作为结果返回

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

我们发现,过啦!
在这里插入图片描述

插入

给定两个整型数字 N 与 M,以及表示比特位置的 i 与 j(i <= j,且从 0 位开始计算)。
编写一种方法,使 M 对应的二进制数字插入 N 对应的二进制数字的第 i ~ j 位区域,不足之处用 0 补齐。具体插入过程如图所示。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/insert-into-bits-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

这题的意思是将N的j到i位置0,然后再把M插入N的j到i位中去。
首先是置0操作,我们可以用一个i到j位为0其余位为1的数去相与,就可以值零,然后再把M移动j位然后位或,就完成了插入
我们来看看代码咋写的

class Solution {
public:
    int insertBits(int N, int M, int i, int j) {
        int k;
        for (int k=i;k<=j;k++) {
            N &= ~((long long)(1<<k));//为了防止溢出强转为longlong,还有按位取反再位与操作
        }
        return N | M << i;
    }
};

我们来看看结果 ,过啦
在这里插入图片描述

只出现一次的数字(2)

给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。
示例 1:
输入:nums = [2,2,3,2]
输出:3
示例 2:
输入:nums = [0,1,0,1,0,1,99]
输出:99
提示:
1 <= nums.length <= 3 * 104
-231 <= nums[i] <= 231 - 1
nums 中,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次

进阶:你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/single-number-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
对数组中的每个元素的每一位进行累加,这一位累加的值对3取余,值为0则多余的数这一位也为0,值为1说明多余的数这一位也为1

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int ret = 0;
        for (int i = 0;i < 32; i++) {//对元素每一位进行操作
            int res = 0;
            for (int j = 0;j < nums.size();j++) {//累加每一位的值
                res += (nums[j]>>i)&1;
            }
            res%=3;//对3取余
            if (res) {//值为1则相加,值为0则不加,中间会空出来
                ret += ((unsigned int)1<<i);//最后累加得出最后的结果
            }
        }
        return ret;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值