前言
欢迎大家积极在评论区留言发表自己的看法,知无不言,言无不尽,养成每天刷题的习惯,也可以自己发布优质的解题报告,供社区一同鉴赏,吸引一波自己的核心粉丝。
今天是五月集训第十天:位运算
一、练习题目
二、算法思路
- 1、191. 位1的个数:位运算,这题目用到了
&
与运算和>>
右移运算。& 1
相当于模2也可以用来判断奇偶数,>> 1
相当于除2。 - 2、461. 汉明距离:用了第一题的函数,对于两个数,如果二进制位相同,则汉明距离位0,不同则为1,这就是异或
^
运算了,巧妙运用异或解决。 - 3、136. 只出现一次的数字:利用异或
^
,因为重复的出现的是偶数,偶数个异或还是0,最后异或上唯一一个出现的数,就是它本身。(异或满足交换律和结合律。) - 4、137. 只出现一次的数字 II:这道题需要一个巧妙的处理,参考了大佬们的思路。对于某个重复出出现三次的数,在不考虑进位的情况下,它对每一位二进制位的贡献是3或者0,只出现一次对每一位二进位的贡献要么是1要么是0,当我们把所有数字的每一位二进制加起来,不考虑进位的情况它的和要么是 3 n + 1 3n+1 3n+1,要么是 3 n 3n 3n,语言有点难懂,举个例子:
[1,1,1,3]
各个位置二进制的情况如下:
0 0 1
0 0 1
0 0 1
0 1 1
各个二进制位置求和:
0 1 4 (3 + 1)
规律就出来了,对每一位二进制位求和后对3取模
,取模的结果是1或者0,如果是1对应着唯一出现一次的数在该位置上也是1,0的话就说明是0,然后把这个数还原出来。还是上面的例子唯一出现的数字是3,在第一个二进制位取模是1,第二个二进制取模也是1,第三个是0,再做一步
1
∗
2
0
+
1
∗
2
1
+
0
∗
2
2
=
3
1*2^0+1*2^1+0*2^2 = 3
1∗20+1∗21+0∗22=3。这题目可以引申出,很多重复四次、五次、六次让你找唯一出现的数,道路是一样的对4、5或者6取模。
三、源码剖析
// 191. 位1的个数
class Solution {
public:
int hammingWeight(uint32_t n) {
int ans = 0;
while(n) {
ans += (n & 1); //(1)
n >>= 1; //(2)
}
return ans;
}
};
- 1、例如:
8
的二进制1000
,用n&1
判断末尾是不是1是的话计数器加1否则加0。 - 2、然后把n右移一位,二进制右移动左边补零的,如
0100
这是8
右移一位后的结果,这样最后就会变成0
。
// 461. 汉明距离
class Solution {
public:
int hammingWeight(uint32_t n) {
int ans = 0;
while(n) {
ans += (n & 1);
n >>= 1;
}
return ans;
} //(1)
int hammingDistance(int x, int y) {
return hammingWeight(x ^ y); //(2)
}
};
- 1、191题学以致用,用了它的函数;
- 2、分析题意转换成两数异或后求位1的个数。
// 136. 只出现一次的数字
class Solution {
public:
int singleNumber(vector<int>& nums) {
int n = nums.size(), ans = 0;
for(int i = 0; i < n; i++) {
ans ^= nums[i]; //(1)
}
return ans;
}
};
- 1、偶数个重复最终是0,0异或唯一的数就是它本身。
// 137. 只出现一次的数字 II
class Solution {
public:
int singleNumber(vector<int>& nums) {
int ans = 0;
for(int i = 0; i < 32; i++) {
int tmp = 0;
for(auto n : nums) {
tmp += (n>>i) & 1;
} //(1)
tmp %= 3; //(2)
if(tmp) {
ans += (1<<i); //(3)
}
}
return ans;
}
};
- 1、逐个二进制位求和;
- 2、对每个二进制位对3取模
- 3、将我们要找的数对应二进制位置置1或0,详情可看算法思路。