题目:
一个整型数组里除了两个数字之外,其他的数字都出现了偶数次。请写程序找出这两个只出现一次的数字。
方法一:
将所有的元素装到map中,检查second要为1,就是要找的数
class Solution {
public:
void FindNumsAppearOnce(vector<int> data,int* num1,int *num2)
{
int len=data.size();
map<int,size_t>result;
for(int i=0;i<len;i++)
result[data[i]]++;
for(auto w:result)
if(w.second==1)
{
*num1=w.first;
break;
}
for(auto w:result)
if(w.second==1)
*num2=w.first;
}
};
方法二:
首先我们考虑这个问题的一个简单版本:一个数组里除了一个数字之外,其他的数字都出现了两次。请写程序找出这个只出现一次的数字。
想到了异或运算的性质:任何一个数字异或它自己都等于0 。也就是说,如果我们从头到尾依次异或数组中的每一个数字,那么最终的结果刚好是那个只出现一次的数字,出现两次的数字全部在异或中抵消掉了。
如果能够把原数组分为两个子数组。在每个子数组中,包含一个只出现一次的数字,而其它数字都出现两次。这样拆分原数组,按照前面的办法就是分别求出这两个只出现一次的数字。
现在已经把原数组分成了两个子数组,每个子数组都包含一个只出现一次的数字,而其它数字都出现了两次。因此到此为止,所有的问题我们都已经解决。
class Solution {
public:
void FindNumsAppearOnce(vector<int> data, int* num1, int *num2)
{
int len = data.size();
if (len<2)
return;
int result = 0;
for (int i = 0; i<len; i++)
result = result^data[i];
int Index = FindIndex(result);
if(Index==0)
return;
*num1 = *num2 =0;
for (int i = 0; i<len; i++)
{
if (data[i] & Index)
*num1 = *num1^data[i];
else
*num2 = *num2^data[i];
}
return;
}
private:
int FindIndex(int x)
{
if(x==0)
return 0;
int i = 1;
while (i<x)
{
if (x&i!= 0)
break;
else
i = i * 2;
}
return i;
}
};
牛客网网站上直接用*num1 = *num2 =0;给指针所指的对象赋值,在VS中,这样的赋值不对(不明原因)。
int a = 0, b = 0;
num1 = &a;
num2 = &b;
下面给出在VS中实现的操作
#include<iostream>
#include<vector>
using namespace std;
int FindIndex(int x)
{
if (x == 0)
return 0;
int i = 1;
while (i<x)
{
if (x&i!= 0)
break;
else
i = i * 2;
}
return i;
}
void FindNumsAppearOnce(vector<int> data, int* num1, int *num2)
{
int len = data.size();
if (len<2)
return;
int result = 0;
for (int i = 0; i<len; i++)
result = result^data[i];
int Index = FindIndex(result);
cout << "Index:" << Index << endl;
if (Index == 0)
return;
int a = 0, b = 0;
for (int i = 0; i<len; i++)
{
if (data[i] & Index)
a = a^data[i];
else
b = b^data[i];
}
num1 = &a;
num2 = &b;
cout <<"*num1:" <<*num1 << endl;
cout <<"*num2:" << *num2 << endl;
return;
}
int main()
{
vector<int>q = { 2, 2, 3, 3, 4, 6, 5, 5};
int *num1=NULL, *num2=NULL;
FindNumsAppearOnce(q, num1, num2);
return 0;
}
或者换种写法
class Solution {
public:
void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) {
int res = 0;
for (int i = 0; i < data.size(); i++){
res = res ^ data[i];
}
int n1 = 0, n2 = 0;
int pos = 1;
while ((res & pos) == 0) // 里面的小括号一定要加
pos = pos * 2;
for (int i = 0; i < data.size(); i++){
if (data[i] & pos)
n1 = n1 ^ data[i];
else
n2 = n2 ^ data[i];
}
*num1 = n1;
*num2 = n2;
}
};
方法三:
用set查找,遍历输入元素,当前数字不在set中则插入,否则从set删除(出现两次的数字),这样结束遍历后set中剩余的就是要找的那两个只出现了一次的数字。
class Solution {
public:
void FindNumsAppearOnce(vector<int> data, int* num1, int *num2)
{
set<int>result;
int len=data.size();
for(int i=0;i<len;i++)
{
auto Index=result.find(data[i]);
if(Index==result.end())
result.insert(data[i]);
else
result.erase(Index);
}
*num1=*result.begin(); //set不支持下标运算
*num2=*(++result.begin());
}
};