Welcome to 9ilk's Code World
(๑•́ ₃ •̀๑) 个人主页: 9ilk
(๑•́ ₃ •̀๑) 文章专栏: 算法Journey
本节我们来赏析位运算的一些进阶题目。
🏠 只出现一次的数字II
📌 题目解析
只出现一次的数字II
📌 算法原理
✏️ 思路一:哈希表+异或
由题目意思可得,除了只出现一次的数字之外,其他数字都出现了三次,我们可以利用哈希表,我们遍历一遍数组,将这些数异或用ret记录,当某个数出现三次时,我们再用ret异或这个数,最后得到的结果就是只出现一次的数字,因为其他数字在我们操作下都出现了四次被异或为0。
参考代码:
class Solution {
public:
int singleNumber(vector<int>& nums)
{
unordered_map<int,int> hash;
int ret = 0;
for(const auto& e : nums)
{
hash[e]++;
ret ^= e;
if(hash[e] == 3) //出现三次再次异或
ret ^= e;
}
return ret;
}
};
✏️ 思路二: bit位分组
由于其他数字都出现3次,因此我将数组中每个数特定bit位相加,对于出现过三次的这些数,他们特定bit位相加得到的一定是3的倍数,%3得到0,最后数组中所有数加完后再%3得到的一定是只出现一次那个数字上的bit位的数字.因此,我们可以不断向左移,对每个bit位进行这样的操作,就能得到只出现一次数字的完整bit位了.
参考代码:
class Solution {
public:
int singleNumber(vector<int>& nums)
{
int ret = 0;
for(int i = 0 ; i < 32 ; i++)
{
int sum = 0;
for(int x : nums)
{
if((x>>i)&1) sum++;
}
sum %= 3;//得到的是只出现一次数字i位上的数字
if(sum == 1) ret |= (1 <<i);
}
return ret;
}
};
拓展 : 如果其他数出现n次,另外一个数字出现m次,我们同样可以利用这种方式来找出不同点(%n)从而来找到只出现一次的数字.
🏠 只出现一次的数字III
📌 题目解析
只出现一次的数字III
-2^31 <= nums[i] <= 2^31 - 1 注意数据范围过大使用int可能会导致数据溢出。
📌 算法原理
由图中我们可以知道,ret最后一定是两个只出现一次的数字(记为a和b)的异或,由于异或是相同为0,相异为1,因此我们可以利用n&(-n)提取异或后的ret最右边的1,也就是a和b在这个bit位上的数字不同,依照这个不同可以将原数组中的数字划分为两种:一种是k位上是1,一种是k位上是0,由于其他数字出现了两次他们异或之后得到的一定是0,最后ret1和ret0得到的一定是只出现一次的两个数字。
参考代码:
class Solution
{
public:
typedef long long ll;
vector<int> singleNumber(vector<int>& nums)
{
ll ret = 0;
for(int i = 0 ; i < nums.size() ; i++)
{
ret ^= nums[i]; //得到的是两个不同的数异或
}
//我们先找ret最右边的1因为异或相异为1 这样就可以依照这个把a分到这一位上有0/1,b分到这一位有1/0
ll k = ret & (-ret);
//用k判断某一位是1还是0
ll ret1 = 0;
ll ret0 = 0;
for(int i = 0 ; i < nums.size();i++)
{
if(nums[i] & k) //说明该位上为1
ret1 ^= nums[i];
else
ret0 ^= nums[i];
}
return {(int)ret1,(int)ret0};
}
}s2;
🏠 消失的两个数字
📌 题目解析
消失的两个数字
📌 算法原理
这道题其实就是丢失的数字 + 只出现一次的数字III。我们可以通过nums的数组长度知道未消失前数字的总数。异或一遍原来数字再异或一遍nums,就可以得到消失两个数字的异或。此时就转化为只出现一次的数字III,可以利用ret&(-ret)提取两者不同的bit位,依据不同再遍历一遍进行分组得到这两个数字。
参考代码:
class Solution {
public:
vector<int> missingTwo(vector<int>& nums)
{
int n = nums.size() + 2;
int ret = 0;
for(int i = 1 ; i <= n ; i++)
{
ret ^= i;
}
for(auto& e : nums)
{
ret ^= e;
}// 1^4
int k = ret & (-ret); //提取最右边的一个1 消失的两个数在第k位不同
int ret1 = 0;
int ret0 = 0;
for(int i = 1 ; i <= n ; i ++)
{
if(i & k)
ret1 ^= i;
else
ret0 ^= i;
}
for(auto& e : nums)
{
if(e & k)
ret1 ^= e;
else
ret0 ^= e;
}
int max = 0;
int min = ret0 > ret1 ? (max = ret0,ret1) : (max = ret1,ret0);
return {min,max};
}
};
完。