题目:输入一个整型数组,数组里除了两个数出现一次之外,其它所有数字出现的次数都是2次,求这两个数字。要求时间复杂度为O(n),空间复杂度为O(1)
1 题目要求时间复杂度为O(n)并且空间复杂度为O(1)。这个时候朴素的方法利用数字来记录出现次数的方案都是不行的。
2 根据题目的特点,只有两个数出现一次,其它的所有数据都是出现2次。如果这两个数是a和b,那么对这个数组异或的结果就是a^b。现在我们就是要考虑怎么把数组分成两部分,一部分含有a,一部分含有b,则每部分异或的结果即为两个数a和b的值
3 因为a肯定不等于b,所以a^b的结果肯定不会等于0,那么我们可以就去这个异或结果中最右边的第一个1的位置,根据这个位置来划分这个数组,这个位置是1的位一部分,不是1的分为一部分。
4 例如数组{2,4,3,6,3,2,5,5},数组异或的结果为2,二进制位0010最右边1的位置为第二位。则我们把数组分成两部分{2,3,6,3,2}中每个数的二进制最右边第二位为1,剩下一部分{4,5,5}中每个数二进制最右边第二位为0。
5 求出两部分之后我们就可以直接对每部分求异或即可求出两个数的值
//找到两个只出现一次的数
void FindNumAppearOnce(int *arrNum, int n){
//空指针和个数小于2都是不合法的数据
if(arrNum == NULL || n < 2){
return;
}
//先求出总的异或的值
int sum = 0;
for(int i = 0; i < n; i++){
sum ^= arrNum[i];
}
//找到sum的右边第一个1的位置
int pos = 0;
while((sum & 1) != 1 && (pos <= 32)){
pos++;
sum >>= 1;
}
//没有找出第一个1的位置
if((pos == 0) || (pos > 32)){
return;
}
//定义两个数为
int numOne = 0;
int numTwo = 0;
//枚举求出
int num = 1<<pos;
cout<<num<<endl;
for(int i = 0; i < n; i++){
if((arrNum[i]&num) != 0){
numOne ^= arrNum[i];
}
else{
numTwo ^= arrNum[i];
}
}
cout<<numOne<<" "<<numTwo<<endl;
}