今天记录三道有关位运算的有趣的算法题,都是找出数组中存在的仅出现一次的数字:
136. 只出现一次的数字 - 力扣(LeetCode)

按照题目要求是,除一个元素外,其余元素均出现了两次,然后让我们找到那个仅出现一次的元素。看到题目立即可以想到哈希表,统计所有的数据出现次数,最后将仅出现一次的数据返回即可,当然接下来的两题均可使用此方法解决。
但题目存在一个需要使用常量空间的限制条件(当然上述方法是可以过题的),若要满足该要求,显然是不能使用容器来统计数据个数的方法的。此时,便需要运用到位运算的知识:对于两个相同的数进行异或,结果为0;任何数与0异或,数值不变 。那么将数组中所有的元素进行异或到一起呢,显然最后仅出现一次的数会留下来 ,而其余两两相同的数则会因为异或而消除。
以下是题解:
cpp
class Solution {
public:
int singleNumber(vector<int>& nums) {
int ret = 0;
for (auto x : nums)
ret ^= x;
return ret;
}
};
137. 只出现一次的数字 II - 力扣(LeetCode)

这题与上道的不同仅仅是其余的元素的出现次数增加到了3。但显然异或的方法是不能用的,需要另辟蹊径。此时,可想到有关数据的二进制存储:对于二进制,数位非1即0,若将n(n > 1)个相同元素,与一个不同的元素的固定比特位相加,再对和模n,不就可以得到那个不同元素在该数位上的值,因此可以:
每次,将数组中所有的数二进制的固定数位相加,最后模3,得到的结果如果为1,ret在该数位就为1;为0,ret在该数位上为0。
因为除结果元素在数组中仅出现1次,其余元素均出现了3次,所有元素的固定比特位相加模3,即可消除除结果元素的数据痕迹,只留下结果元素在该比特位的值。
以下是题解:
cpp
class Solution {
public:
int singleNumber(vector<int>& nums) {
int ret = 0;
for (int i = 0; i < 32; ++i)
{
int bit = 0;
for (auto x : nums)
bit += ((x >> i) & 1);
ret |= (1 << i) * (bit % 3);
}
return ret;
}
};
显然,n也可以取2,此时便于前一题的条件相同,所以前一题也可以这样解决:
cpp
class Solution {
public:
int singleNumber(vector<int>& nums) {
int ret = 0;
for (int i = 0; i < 32; ++i)
{
int bit = 0;
for (auto x : nums)
bit += ((x >> i) & 1);
ret |= (1 << i) * (bit % 2);
}
return ret;
}
};
260. 只出现一次的数字 III - 力扣(LeetCode)

此题与第一题的不同就是仅出现一次的元素增加到了2个。显然,前面的方法并不能直接套用解决。但运用第一题的第一个解法与二进制的存储方式(原码,反码和补码)可以提供解题思路:
(以题中例一举例)对数组所有元素进行异或得到tmp = 6,-tmp = -6,对两数的二进制表示:
tmp:> 00000000 00000000 00000000 00000110
-tmp:> 11111111 11111111 11111111 11111010
此时对两数进行&运算,可以发现除了第二数位为1,其余数位都为0。
tmp & -tmp:> 00000000 00000000 00000000 00000010
这样就可以得到原数组中结果元素相异的最小相异数位,通过该值对原数组所有元素相与来进行分组,相同的组进行异或运算,相同元素均会处在相同的组而消除,而结果两元素由于在该数数位相异而分开,最后仅有两结果元素会留下。
以下为题解:
cpp
class Solution {
public:
vector<int> singleNumber(vector<int>& nums) {
long long tmp = 0;
for (auto val : nums)
tmp ^= val;
int x = 0, y = 0;
/*
二进制表示(以示例一举例):
tmp(6): 00000000 00000000 00000000 00000110
-tmp(-6): 11111111 11111111 11111111 11111010
想与可以得到结果的两数二进制中最小一位相异的数位
*/
tmp &= -tmp;
for (auto val : nums)
(tmp & val) != 0 ? x ^= val : y ^= val;
return {x, y};
}
};