前言
位运算一共分为两大类,逻辑运算符和位移运算符
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;
}
};