位运算概念
位运算包括五种:与、或、异或、左移、右移。
异或
- 两种运算律
- 0^任何数字=它自己
- 数字^数字=0
例题
以下例题和剑指:数组中数字出现的次数相同,可一并解决。
1、只出现一次的数字 I
- 数组中只有一个数字出现一次,其他数字都出现了两次。题解如下:
class Solution {
public:
int singleNumber(vector<int>& nums) { //给非空整数数组,返回只出现一次的数字
int ret=0;
for(auto x:nums){
ret ^= x;
}
return ret;
}
};
2、只出现一次的数字 II
- 数组中有两个数字出现了一次,其他数字都出现了两次,输出这两个出现一次的数字。题解如下:
class Solution {
public:
vector<int> singleNumber(vector<int>& nums) {
//想办法把数组拆成两半,每一半都有一个落单的数字,难点在于怎么分成两半。要用左移运算
int x = 0, y = 0, n = 0, m = 1; //设置四个起始参数
for(int num : nums) // 1. 遍历异或 得到落单的两个数字的异或结果:a^b
n ^= num;
while((n & m) == 0) // 2. 循环左移,计算 m 找到第一个为1的位置 并用m作为分开数组的依据
m <<= 1;
for(int num : nums) { // 3. 再遍历 nums 一边遍历一边异或
if(num & m) x ^= num; // 4. 当 num & m != 0 分为一组
else y ^= num; // 4. 当 num & m == 0 分为另一组
}
return vector<int> {x, y}; // 5. 返回出现一次的数字
}
};
3、只出现一次的数字 III
- 数组中有一个数字出现了一次,其他数字出现了三次。求这个落单的数字。
- 本题除了异或,其他四种位运算都用到了,很综合。题解如下:
class Solution {
public:
int singleNumber(vector<int>& nums) {
int res = 0;
for (int i = 0; i < 32; i++) { // 每个数字的32位二级制形式
//遍历每一位,先求和,再%3,最后返回的就是落单的数字
int sum = 0;
for (int num : nums) {
sum += ((num >> i) & 1);
}
if (sum % 3 == 1) res |= (1 << i);
}
return res;
}
};