1、题目描述
一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
2、代码源码
2.1异或运算:
主要用到了位运算中中的异或运算。异或运算有很多很好的性质,关于异或运算的性质见博客http://blog.csdn.net/gtkknd/article/details/52798337
这里说下几个重要的性质:异或运算
(1)任何数据异或0x11111111,结果是数据的取反后的数值
(2)任何数据异或0x0之后,结果是数据本身的值。
(3)一个序列,如果只有一个数出现一次,其余的数都出现2次,那么所有的数全部异或后,结果是出现一次的数。
2.2算法原理:
因为如果一个数据系列中一个数出现了一次,其余的都出现了2次,可以用连续异或的结果的判别,所以这个题目关键是如何将两个出现一次的数分到小序列中再分别利用异或的性质进行判别即可。序列分组:首先所有的依次异或运算操作,得到数据其实就是两个只出现一次的数的异或的结果X,注意异或的性质,不同则1,因此X中如果某位位1,那么这出现一次的这2个数据的这个位的值肯定不一样,因此可以根据这位是0还是1将所有的数据分组,因为只有这样可以保证两个出现一次的数被分到不同的两个子序列中,这2个系列,分别只有一个数出现一次,其余的数都是出现两次的。
2.3代码:
#include<iostream>
#include<vector>
using namespace std;
//返回一个数据二进制第一个1的下标
int findFirst1InRes(int result)
{
int index=0;
while(result)
{
if(result&1)
return index;
index++;
result=result>>1;
}
return index;
}
//根据标志位判断一个数据属于哪个小组
bool cmpData(int data,int index)
{
while(index)
{
data=data>>1;
--index;
}
if(data&1)
return true;
else
return false;
}
class Solution {
public:
void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) {
if(data.empty()||data.size()<2)
return;
int sum=0;
//所有的数全部异或
for(int i=0;i<data.size();i++)
sum = sum^data[i];
//找到从右向左第一个为1的位的下标
int index = findFirst1InRes(sum);
for(int i=0;i<data.size();i++)
{
if(cmpData(data[i], index))
*num1 ^=data[i];
else
*num2 ^=data[i];
}
}
};
int main()
{
int data[] = {2,4,3,6,3,2,5,5};
vector<int> vec;
for(int i=0;i<8;i++)
vec.push_back(data[i]);
Solution A;
int num1=0, num2=0;
A.FindNumsAppearOnce(vec, &num1, &num2);
system("pause");
return 1;
}
注意:
这个题目十分强调对异或的理解程度。异或最主要的性质:多个数连续做异或运算,多个数中只有一个数出现奇数次,其余的数都出现了偶数次,那么最后异或运算的结果就是那个唯一出现奇次的数据,与多个数据的顺序无关,eg:abbcc 和 bcacb全部异或的结果是一样的,但是需要注意的异或本身不存在交换律,比如abc和bca连续异或的结果是不一样的。