0
点赞
收藏
分享

微信扫一扫

《位运算技巧以及Leetcode的一些位运算题目》


目录

  • ​​技巧​​
  • ​​练习位运算​​
  • ​​[461. 汉明距离](https://leetcode-cn.com/problems/hamming-distance/)​​
  • ​​[190. 颠倒二进制位](https://leetcode-cn.com/problems/reverse-bits/)​​
  • ​​[136. 只出现一次的数字](https://leetcode-cn.com/problems/single-number/)​​
  • ​​[260. 只出现一次的数字 III](https://leetcode-cn.com/problems/single-number-iii/)​​
  • ​​[268. 丢失的数字](https://leetcode-cn.com/problems/missing-number/)​​
  • ​​[693. 交替位二进制数](https://leetcode-cn.com/problems/binary-number-with-alternating-bits/)​​
  • ​​[476. 数字的补数](https://leetcode-cn.com/problems/number-complement/)​​
  • ​​练习二进制思想​​
  • ​​[342. 4的幂](https://leetcode-cn.com/problems/power-of-four/)​​
  • ​​[318. 最大单词长度乘积](https://leetcode-cn.com/problems/maximum-product-of-word-lengths/)​​
  • ​​[338. 比特位计数](https://leetcode-cn.com/problems/counting-bits/)​​
  • ​​一些补充​​
  • ​​剑指 Offer 56 - I. 数组中数字出现的次数​​


技巧

位运算的一些基础技巧

x ^ 0... = x
x ^ 1... = ~x
x ^ x = 0
x & 0... = 0
x & 1... = x
x & x = x
x | 0... = x
x | 1... = 1...
x | x = x

进阶技巧

n & (n-1) 可以去除n的位级表示中最低的一位:
n = 11110100 , n - 1 = 11110011
n & (n-1) = 11110000
n & (-n) 可以得到n的位级表示中最低的那一位:
n = 11110100 , 取负:-n = 00001100
n & (-n) = 00000100

练习位运算

​​461. 汉明距离​​

先异或运算,然后统计1个数:

class Solution {
public:
int hammingDistance(int x, int y) {
int XOR_result = x ^ y;
int result = 0;
while(XOR_result != 0)
{
result += (XOR_result & 1);
XOR_result = XOR_result >> 1;
}
return result;
}
};

​​190. 颠倒二进制位​​

result不断左移,最低位加上n的最低位,n不断右移。

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

​​136. 只出现一次的数字​​

思路一:哈希先扫一遍,然后再扫一遍,找到second为1的it。

class Solution {
public:
int singleNumber(vector<int>& nums) {
unordered_map<int,int> umap;
int result = 0;
for(int i = 0; i < nums.size(); i++)
{
umap[nums[i]]++;
}
for(auto it : umap)
{
if(it.second == 1) return it.first;
}
return 0;
}
};

思路二:利用 x ∧ x = 0 和 x ∧ 0 = x 的特点,将数组内所有的数字进行按位异或。出现两次
的所有数字按位异或的结果是 0,0 与出现一次的数字异或可以得到这个数字本身。神仙思路!

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

​​260. 只出现一次的数字 III​​

这次是有两个不同的元素,那么用位运算如何做呢?
觉得这个题解写的很好,浅显易懂。
​​​某题解​​​ 编程时注意一下细节:
1、&的优先级比==低,所以需要加括号
2、用int作为res会报错,需要用long

class Solution {
public:
vector<int> singleNumber(vector<int>& nums) {
long res = 0;
for(int i = 0; i < nums.size(); i++)
{
res = res ^ nums[i];
}
//此时res = a ^ b;
//找出1的位级表示中最低的那一位,代表着a与b在这一位是不一样的,假设a在这一位为0,b在这一位为0
res = res & (-res);
int a = 0;
int b = 0;
for(int i = 0; i < nums.size(); i++)
{
if((nums[i] & res) == 0) //不可能b,而且除了a其他符合要求的都是重复的
a = nums[i] ^ a;
else
b = nums[i] ^ b;
}
return {a,b};
}
};

​​268. 丢失的数字​​

XOR原理:x ^ x = 0; 0 ^ y =y;
两个相同的数字XOR之后为0,如果这个数字只出现了一次,那么XOR之后还是本身。这一题本质上和上一题是一样的。
一些细节,看注释

class Solution {
public:
int missingNumber(vector<int>& nums) {
int n = nums.size();
int result = n; //因为for循环中i不能为n,而我们必须遍历[0,n],所以初值设置为n
for(int i = 0; i < n; i++) //遍历nums数组,其丢失数在nums[] + [0..n]中只出现了一次
{
result = result ^ i ^ nums[i];
}
return result;
}
};

​​693. 交替位二进制数​​

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

​​476. 数字的补数​​

思路一:一个一个来,注意倒序。

class Solution {
public:
int findComplement(int n) {
int result = 0;
vector<int> res;
while(n)
{
res.emplace_back(!(n & 1));
n = n >> 1;
}
for(int i = res.size() - 1; i >=0; i--)
{
result = (result << 1) | res[i];
}
return result;
}
};

当然也可以使用栈来做,思路一样的:

class Solution {
public:
int findComplement(int n) {
int result = 0;
stack<int> res;
while(n)
{
res.push(!(n&1));
n >>= 1;
}
while(!res.empty())
{
result = (result << 1) | res.top();
res.pop();
}
return result;
}
};

思路二:使用异或
举例:
5的二进制是:0101,7的二进制是: 0111,它们的抑或为:0010,去掉前导零位即为取反。
再来一个例子,假设a为1110 0101,b为1111 1111,a^b = 0001 1010是a的取反。也就是说二进制位数与num相同,且全为1的数tmp与num的抑或即为所求。

class Solution {
public:
int findComplement(int n) {
int tmp=0;
int tmp_n = n;
while(n)
{
tmp = (tmp << 1) | 1;
n >>= 1;
}
return tmp ^ tmp_n;
}
};

练习二进制思想

​​342. 4的幂​​

先考虑2的次方:
如果n为2的整数次方,那么它的二进制一定是​​​0...1...0​​​的形式;那么n-1的二进制应该是​​0..011...1​​​的形势。这两个数按位求与的结果一定为0。
如果n为4的整数次方,它一定为2的偶数幂。所以4的幂与二进制数(1010101010…10)相与会得到0.
int 为32位,每4位1010对应16进制a,所以应该为0xaaaaaaaa;
注意&与==的优先级。

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

​​318. 最大单词长度乘积​​

怎样快速判断两个字母串是否含有重复数字呢?可以为每个字母串建立一个长度为26的二进制数字,每个位置表示是否存在该字母。如果两个字母串含有重复数字,那它们的二进制表示的按位与不为0。同时,我们可以建立一个哈希表来存储字母串(在数组的位置)到二进制数字的映射关系,方便查找调用。

class Solution {
public:
int maxProduct(vector<string>& words) {
unordered_map<int,int> hashmap; //使用哈希表来存储(位掩码 -> 单词长度)
int ans = 0;
for(auto word : words) //遍历每个string
{
int mask = 0; //位掩码
int size = word.size(); //单词长度
for(auto c : word) //encode位掩码
mask = mask | (1 << (c-'a'));
hashmap[mask] = max(hashmap[mask],size);//如果掩码相同,存储更长的字符串(说明有些字符重复)
//对比当前单词与之前的所有单词,无重复字符,且长度乘积大于ans则更新ans
for(auto it : hashmap)
{
if((mask & it.first)== 0) //如果无重复字符
{
ans = max(ans,size*it.second);
}
}
}
return ans;
}
};

​​338. 比特位计数​​

利用dp+位运算。
定义一个数组dp,dp[i]表示数字i的二进制含有1的个数。
对于第i个数字,如果它二进制的最后一位为1,那么它含有1的个数则为dp[i] = dp[i-1]+1;
如果它二进制的最后一位为0,那么它含有1的个数和其右移结果相同,即dp[i] = dp[i>>1]

class Solution {
public:
vector<int> countBits(int num) {
vector<int> dp(num+1,0);
for(int i = 1; i <= num; i++)
{
if((i & 1) == 1)
dp[i] = dp[i-1] + 1;
else
dp[i] = dp[i>>1];
}
return dp;
}
};

一些补充

2021.7.4

剑指 Offer 56 - I. 数组中数字出现的次数

一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。

示例 1
输入:nums = [4,1,4,6]
输出:[1,6] [6,1]
示例 2
输入:nums = [1,2,10,4,1,4,3,3]
输出:[2,10] [10,2]
限制:
2 <= nums.length <= 10000

两个只出现依次的数字位x,y
x和y二进制至少有一位不同,根据此位可以将nums划分成分别包含x和y的两个子数组。
分别对两个子数组遍历执行异或操作,可以得到两个只出现一次的数字x,y。
1、遍历nums数组执行异或,得到结果 x^y
2、x^y某二进制位为1,则x和y的此二进制位一定不同。
初始化一个辅助变量m = 1,从右向左循环判断,可以得到x^y的首位1,记录在m中。
3、拆分nums为两个子数组
4、分别遍历两个子数组执行异或
for(num : nums)
if(num & m == 1) x ^= num;
else y ^= num;
return x,y;
最终代码:

vector<int> singleNumbers(vector<int>& nums)
{
int ret = 0;
for(int n : nums)
ret ^= n; // x^y
int div = 1;
while((ret & div) == 0)
div <= 1;
int x = 0, y = 0;
for(int n : nums)
{
if(div & n == 0)
a ^= n;
else
b ^= n;
}
return vector<int> {x,y};
}


举报

相关推荐

0 条评论