题目:

一个整型数组里除了两个数字之外,其他的数字都出现了偶数次。请写程序找出这两个只出现一次的数字。

方法一:

将所有的元素装到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());
}
};