力扣题目分享:只出现1次的数字I II III(位运算版)
只出现一次的数字
这道题目是要找出只出现一次的数字,其他数字都会出现两次
C语言可以直接用的位运算有按位与(&),按位或(|),按位异或(^)
这道题用到的是按位异或
先讲一下按位异或:
对于两个二进制数,相同为0,不同为1,上图中前两位是不同的,所以只有前两位为1,后三位都为1,所以这两个数按位异或的结果就是00011,即3
那两个相同的数按位异或的结果就是0
而0和任何数异或都会是另一个数
我们可以利用这个特性,来把所有的数都异或一遍,最后的结果就是单独的那个数了(相同的都变成0了,0和任何数异或都会是另一个数)
本题代码:
class Solution {
public:
int singleNumber(vector<int>& nums)
{
int num = 0;
for(auto e : nums)
{
num^=e;
}
return num;
}
};
只出现一次的数字II
本题和上题的唯一区别就是从出现两次变成了三次,那肯定不能再用按位异或了,这样异或出来的结果完全没有任何意义
但这一题还是考的位运算操作
先来讲讲按位与和按位或
按位与是双方位都为1才是1,按位或只需要双方有1个为1就是1
思路:
因为除了那一个数,其他数都出现三次,可以先用按位与来遍历每一个数,用一个[32]的数组把每一个位中的1都统计起来,只要有一位的1的个数不是3的倍数,那多出来的1就是多余的那个数的
具体实现:
class Solution {
public:
int singleNumber(vector<int>& nums)
{
int er[32]={0};
for(auto e : nums)
{
for(int i=0;i<32;i++)
{
if(e&(1<<i))//如果第i位是1
{
er[i]++;//那么第i位++
}
}
}
int p=0;
for(int i = 0;i<32;i++)
{
if(er[i]%3 != 0)//如果这一位1的个数不是3的倍数,那多出来的那个1就是属于只出现一次的数的
{
p |= (1<<i);//将p的第i位置成1
}
}
return p;
}
};
只出现一次的数字III
该题和第一题的区别是只出现一次的数字从1个变成了2个,那有没有什么办法可以将它转换成2个第一题呢?
可以确定的是,这两个只出现一次的数不是一样的,如果将这全部的数都异或,最后是两个只出现一次的数异或的结果,我们就可以通过这个结果来给它分组
具体实现:
class Solution {
public:
vector<int> singleNumber(vector<int>& nums)
{
int num=0;
for(auto e : nums)//将所有数都异或一遍,得到的结果就是只出现一次的那两个数异或的结果
{
num^=e;
}
int i;
for(i=0;i<32;i++)//判断这个结果的哪一位是1,只要是1就代表这两个数的这一位不同
{
if(num & (1 << i))//不同就跳出
break;
}
int num1=0,num2=0;
for(auto e : nums)//然后根据这个不同的位来对这些数据分组,这样这两个只出现一次的数就被分到两组了
{
if(e & (1 << i))
num1 ^= e;//就化解成了问题1了
else
num2 ^= e;
}
vector<int> v;
v.push_back(num1);
v.push_back(num2);
return v;
}
};