- 常用技巧
- 基础
- 461. 汉明距离(计算两数二进制位不同的 个数)">461. 汉明距离(计算两数二进制位不同的 个数)
- 190. 颠倒二进制位(二进制位前后颠倒)">190. 颠倒二进制位(二进制位前后颠倒)
- 136. 只出现一次的数字(异或)">136. 只出现一次的数字(异或)
- 693. 交替位二进制数">693. 交替位二进制数
- 二进制特性
- 342. 4的幂(log以及特性)">342. 4的幂(log以及特性)
- 318. 最大单词长度乘积(二进制占位)">318. 最大单词长度乘积(二进制占位)
- 338. 比特位计数(动态规划)">338. 比特位计数(动态规划)
- 693. 交替位二进制数(善用位运算符)">693. 交替位二进制数(善用位运算符)
- 476. 数字的补数(注意条件)">476. 数字的补数(注意条件)
常用技巧
位运算是算法里比较特殊的一种类型,他们利用二进制位运算的特性进行一些奇妙的优化和计算。常用的位运算符包括:“^”异或、“&”与、“|”或、“~”非、算数左移“<<”,算数右移“>>”
n&(n-1)可以去掉最低一位,
n&(-n)可以得到二进制数的最后一个1所表示的二进制
基础
461. 汉明距离(计算两数二进制位不同的 个数)
class Solution {
public:
int hammingDistance(int x, int y) {
int a=x^y;
int sum=0;
while(a!=0){
sum+=a&1;
a=a>>1;
}
return sum;
}
};
190. 颠倒二进制位(二进制位前后颠倒)
用新的数据来存储,一个左移,一个右移
class Solution {
public:
uint32_t reverseBits(uint32_t n) {
uint32_t ans = 0;
for(int i = 0; i < 32; ++i){
ans <<= 1;
ans += n & 1;
n >>= 1;
}
return ans;
cout<<ans<<endl;
}
};
//定义一个 32 位int ans来存储结果,每次左移,然后原数据右移
136. 只出现一次的数字(异或)
class Solution {
public:
int singleNumber(vector<int>& nums) {
int ans = 0;
for(const int & num : nums){
ans^=num;
}
return ans;
}
};
693. 交替位二进制数
遍历
注意优先级问题
class Solution {
public:
bool hasAlternatingBits(int n) {
int pre = n&1;
n = n >> 1;
while(n) {
if(pre == (n&1)) return false;
pre = n&1;
n = n >> 1;
}
return true;
}
};
异或
对输入 n 的二进制表示右移一位后,得到的数字再与 n 按位异或得到 a。当且仅当输入 n 为交替位二进制数时,a 的二进制表示全为 1(不包括前导 0)。这里进行简单证明:当 a 的某一位为 1 时,当且仅当 n 的对应位和其前一位相异。当 a 的每一位为 1 时,当且仅当 n 的所有相邻位相异,即 n 为交替位二进制数。
class Solution {
public:
bool hasAlternatingBits(int n) {
long a = n ^ (n >> 1);
return (a & (a + 1)) == 0;
}
};
二进制特性
342. 4的幂(log以及特性)
log
double fmod(double x, double y)返回x除以y的余数
class Solution {
public:
bool isPowerOfFour(int n) {
double a ;
a = log10(n)/log10(4);
return fmod(a,1)==0;
}
};
二进制数特性
如果这个数是2的整数次方。那么它的二进制为1的个数只有一个。如果这个数是4的整数次方,那么二进制中表示1的位置一定是奇数位。
n&n-1可以去除位级中表示最低的一位。
1431655765是所有奇数位置1的结果class Solution {
public:
bool isPowerOfFour(int n) {
return n > 0&& !(n &(n-1))&&(n&1431655765);
}
};
318. 最大单词长度乘积(二进制占位)
思想:通过二进制的方式来表达这个a-z的字符在字符串中是否存在。
为了方便遍历,使用hash表来存储数据,key为字符串在二进制数中的位置,value为相同占位字符串的最大长度
通过一个26位的二进制数来表示,字符a-z在字符串中存在的情况
class Solution {
public:
int maxProduct(vector<string>& words) {
unordered_map <int , int> hash;//建立哈希表,两个值分别为mask和最大长度
int ans = 0;//最大值
for(const string &word : words){
int mask = 0, size = word.size();//size是字符串的长度
for(const char &c : word){
mask |= 1 <<(c - 'a');//此处的等式右边表示每个字母对应的二进制数表示形式
//此处的mask就代表着前一个字符串与现在的字符串相或的结果,如果有相同的字母,或为1
}
hash[mask] = max(hash[mask], size);//构建一个哈希表,存储最大值
for(const auto&[h_mask,h_len]:hash){
if(!(mask & h_mask)){
ans = max(ans, size*h_len);
}
}
}
return ans;
}
};
338. 比特位计数(动态规划)
巧妙,只能说巧妙
分为两种情况,最低位为1和最低位为0。最低位为1的话就代表i-1的最低位为零,直接dp[i-1]即可
- 最低位为0的话,经过移位1的数量不变,因此直接dp[i>>1]即可
```cpp
class Solution {
public:
vector
countBits(int num) {
}vector<int> dp(num+1, 0); for(int i = 1; i <= num; ++i) dp[i] = i & 1? dp[i-1] + 1: dp[i>>1];//往左移动一位相当于变小,因为最后一位是0所以1的个数并没有发生改变 return dp;
};
<a name="vQDvN"></a>
### [268. 丢失的数字](https://leetcode-cn.com/problems/missing-number/)(只出现一次数字的变形题)
- 题目让求一个数组中没有出现的0~n中的数字是那一个
- 这个数组加上0~n的所有数字。就是求只出现一次的数字的题目了
```cpp
class Solution {
public:
int missingNumber(vector<int>& nums) {
int res = 0;
int n = nums.size();
for (int i = 0; i < n; i++) {
res ^= nums[i];
}
for (int i = 0; i <= n; i++) {
res ^= i;
}
return res;
}
};
693. 交替位二进制数(善用位运算符)
class Solution {
public:
bool hasAlternatingBits(int n) {
int a = 1&n;
n >>= 1;
while(n>0){
if((n&1) == a) return false;
a = 1&n;
n >>= 1;
}
return true;
}
};
476. 数字的补数(注意条件)
- 最高有效位,左边的为0的二进制位不应该参与运算
- 因此先求得最高有效位在哪里,然后进行设置掩码mask全为1,进行异或运算即可取反。
class Solution { public: int findComplement(int num) { int highbit = 0; for (int i = 1; i <= 30; ++i) { if (num >= (1 << i)) { highbit = i; } else { break; } }//求为1的最高位 highbit int mask = (highbit == 30 ? 0x7fffffff : (1 << (highbit + 1)) - 1); return num ^ mask; } };