只出现一次的数字中的三道算法题,无疑是我们在刚刚学习位运算很好的练习题,尤其其中的第三道,我认为思路非常的精彩。
1. 只出现一次的数字
这里讲一个异或运算的规则:
a ^ b = b ^ a
a ^ a = 0
这道题目,将所有的值异或起来,相同的两个值异或为0,这样就只剩下单独的那个数了
class Solution {
public:
int singleNumber(vector<int>& nums) {
int x = 0;
for (int num : nums)
x ^= num;
return x;
}
};
2. 只出现一次的数字 II
思路:对于数组中的所有数,如果我们只看最后一个bit位,他们只会出现:
3个0 + 出现一个那个数为0 将他 MOD 3 = 0
3个0 + 出现一个那个数为1 将他 MOD 3 = 1
3个1 + 出现一个那个数为0 将他 MOD 3 = 0
3个1 + 出现一个那个数为1 将他 MOD 3 = 1
32个位也是一样的~
这样就可以推广一下,相同的数出现N次相加上只出现一次的数,最后MOD N 后就是只出现一次的值
class Solution {
public:
int singleNumber(vector<int>& nums) {
int res = 0;
for (int i = 0; i < 32; ++i) {
int cnt = 0;
for (auto x : nums) {
cnt += (x>>i)&1;
}
res |= (cnt%3)<<i;
}
return res;
}
};
3. 只出现一次的数字 III
直接说思路和难点,
难点:如何在数组中区分出来两个值?全部异或在一起后又如何区分?
思路:将所有的值全部异或在一起,那么就能得到这两个数异或起来的结果。因为异或的结果表示的是二进制位不同的地方。所以我们可以找到第一个不同的结果
编辑
通过这个第一个不同的二进制位,将数组中全部的值分为两组,两组分别异或在一起就可以得到这两个值。
class Solution {
public:
vector<int> singleNumber(vector<int>& nums) {
vector<int> ret = { 0,0 };
int tmp = 0;
for (auto x : nums)
{
tmp ^= x;
}
int pos = 0;
for (int i = 0; i <= 31; ++ i)
{
if ((tmp >> i) & 1)
{
pos = i;
break;
}
}
for (auto x : nums)
{
if ((x >> pos) & 1)
ret[0] ^= x;
else
ret[1] ^= x;
}
return ret;
}
};
4. 总结
位运算最常用在嵌入式开发中,熟练掌握位运算,可以在单片机中宛若游龙~