面试OR笔试22——数组中只出现一次的数字

来源:互联网 发布:mac装windows系统好吗 编辑:程序博客网 时间:2024/06/03 12:27

1 题目及要求

1.1 题目描述

一个整数数组中除了两个数字只出现一次之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)

例如输入数组{2, 4, 3, 6, 3, 2,5, 5}只有46只出现了一次,其他的都出现了两次,所以输出是46

 

 

2 解答

2.1 题目分析

只有一个数字只出现一次时,用异或运算的性质:任何数和本身的异或得0。也就是说从头到尾依次异或数组中的每个数字,那么最终的结果刚好是那个只出现一次的数字,因为那些成对出现两次的数字全部在异或中抵消了。

因此对于两个出现一次的数字,我们试着把原数组分成两个子数组,使得每个数组只包含一个只出现一次的数字,而其他的数字都是成对地出现两次。

首先,我们还是从头到尾依次异或数组中的每个数字,那么最终结果就是两个只出现一次的数字的异或的结果。因为其他数字都出现了两次,在异或中全部抵消了。由于这两个数字肯定不一样,那么异或的结果肯定不是0,也就是说起二进制表示中至少有一位是1。我们在结果中找到某一位为1的位置(代码实现的是最低位的1),记为第bit位。然后我们以第bit位是不是1把原数组的数字分成两组,第一组中的数字的bit位都为1,第二组的为0。那么两个相同的数字一定被分到同一组中,而两个只出现一次的数字一定被分到不同的组中。这样每个子数组都只有一个数字只出现一次,其他的都出现两次。

 

2.2 代码

#include <iostream>using namespace std;unsigned findFirstBit1(int n){unsigned bit = 0;for(bit=1;bit<sizeof(int)<<3 && !(n&1<<bit);++bit);return bit;}void findTwoNumbersAppearOnce(int *v, int n, int *num){if(!v || !num || n<2) return;int resXor = 0;for(int k1(0);k1<n;++k1) resXor ^= v[k1];int difBit = 1<<findFirstBit1(resXor);// int difBit = rexXor & (~resXor+1);// 该句和更加简洁num[0] = num[1] = 0;for(int k1(0);k1<n;++k1)if(v[k1]&difBit) num[0] ^= v[k1];else num[1] ^= v[k1];}int main(){int v[] = {2,4,3,6,3,2,5,5};int res[2];findTwoNumbersAppearOnce(v,8,res);cout << res[0] << ' ' << res[1] << endl;    return 0;}