数组中只出现一次的数字
题目描述
一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
思路:
- 先来看一个比较简单的情况,如果数组中只有一个数字出现一次,其他都出现两次。那么我们应该可以想到异或运算。异或运算有一个比较好的性质是:相同为0,相异为1。也就是说,任何一个数字异或它自己都等于0,而0异或任何数都等于那个数。因此,从头到尾依次异或数组中的每个数字,那么最终结果刚好是那个只出现一次的数字,重复的数字在异或过程中被抵消了。对于这道题,可以把数组分为两个数组,这数组中只有一个数字是出现一次的,其他的都出现两次,然后对这两个数组分别遍历异或。这样就找到了这两个数字。
具体思路
- 采取异或思想,可以遍历数组,进行异或,由于数组中除了两个数字之外,其他的数字都出现了两次,这些成对的数字进行异或就相互抵消了,所以数组最后异或结果为两个只出现一次数字的异或,由于这两个数字不同,所以异或结果肯定不为0,也就是这个异或结果一定至少有一位是1,我们在结果中找到第一个为1的位的位置,记为第n位。接下来,以第n位是不是1为标准,将数组分为两个子数组,第一个数组中第n位都是1,第二个数组中第n位都是0。这样,便实现了我们的目标。最后,两个子数组分别异或则可以找到只出现一次的数字。
具体代码
class Solution {
public:
void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) {
int length = data.size();
if(length < 2){
return;
}
// 对原始数组每个元素求异或
int resultExclusiveOR = 0;
for(int i = 0; i < length; ++i){
resultExclusiveOR ^= data[i];
}
int indexOf1 = FindFirstBitIs1(resultExclusiveOR);
*num1 = *num2 = 0;
for(int j = 0; j < length; j++){
if(IsBit1(data[j], indexOf1)){
*num1 ^= data[j];
}
else{
*num2 ^= data[j];
}
}
}
private:
// 找到二进制数num从右往左数第一个为1的位数,比如0010,第一个为1的位数是2。
int FindFirstBitIs1(int num){
int indexBit = 0;
// 只判断4个字节的,最大不能超过num取值范围
while((num & 1) == 0 && (indexBit < 8 * sizeof( int))){
num = num >> 1;
indexBit++;
}
return indexBit;
}
// 判断第indexBit位是否为1
bool IsBit1(int num, int indexBit){
num = num >> indexBit;
return (num & 1);
}
};