【链接】:​​http://ac.jobdu.com/problem.php?pid=1351​​​
【题目】:
题目1351:数组中只出现一次的数字
时间限制:1 秒内存限制:32 兆特殊判题:否提交:3381解决:979
题目描述:
一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
输入:
每个测试案例包括两行:
第一行包含一个整数n,表示数组大小。2<=n <= 10^6。
第二行包含n个整数,表示数组元素,元素均为int。
输出:
对应每个测试案例,输出数组中只出现一次的两个数。输出的数字从小到大的顺序。
样例输入:
8
2 4 3 6 3 2 5 5
样例输出:
4 6
【思路】:
分析:这是一道很新颖的关于位运算的面试题。
首先我们考虑这个问题的一个简单版本:一个数组里除了一个数字之外,其他的数字都出现了两次。请写程序找出这个只出现一次的数字。
这个题目的突破口在哪里?题目为什么要强调有一个数字出现一次,其他的出现两次?我们想到了异或运算的性质:任何一个数字异或它自己都等于0。也就是说,如果我们从头到尾依次异或数组中的每一个数字,那么最终的结果刚好是那个只出现依次的数字,因为那些出现两次的数字全部在异或中抵消掉了。
有了上面简单问题的解决方案之后,我们回到原始的问题。如果能够把原数组分为两个子数组。在每个子数组中,包含一个只出现一次的数字,而其他数字都出现两次。如果能够这样拆分原数组,按照前面的办法就是分别求出这两个只出现一次的数字了。
我们还是从头到尾依次异或数组中的每一个数字,那么最终得到的结果就是两个只出现一次的数字的异或结果。因为其他数字都出现了两次,在异或中全部抵消掉了。由于这两个数字肯定不一样,那么这个异或结果肯定不为0,也就是说在这个结果数字的二进制表示中至少就有一位为1。我们在结果数字中找到第一个为1的位的位置,记为第N位。现在我们以第N位是不是1为标准把原数组中的数字分成两个子数组,第一个子数组中每个数字的第N位都为1,而第二个子数组的每个数字的第N位都为0。
现在我们已经把原数组分成了两个子数组,每个子数组都包含一个只出现一次的数字,而其他数字都出现了两次。因此到此为止,所有的问题我们都已经解决。
基于上述思路,我们不难写出如下代码:
【代码】:

#pragma comment(linker,"/STACK:102400000,102400000")
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <set>
#include <stack>
#include <math.h>
#include <map>
#include <queue>
#include <deque>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxn = 1e6+10;
const LL MOD = 999999997;
int dir4[4][2]= {{1,0},{0,1},{-1,0},{0,-1}};
int dir8[8][2]= {{1,0},{1,1},{0,1},{-1,1},{-1,0},{-1,-1},{0,-1},{1,-1}};
/*Super waigua */
#define INLINE __attribute__((optimize("O3"))) inline
INLINE char NC(void)
{
static char buf[100000], *p1 = buf, *p2 = buf;
if (p1 == p2) {
p2 = (p1 = buf) + fread(buf, 1, 100000, stdin);
if (p1 == p2) return EOF;
}
return *p1++;
}
INLINE void read(int &x) {
static char c; c = NC(); int b = 1;
for (x = 0; !(c >= '0' && c <= '9'); c = NC()) if(c == '-') b = -b;
for (; c >= '0' && c <= '9'; x = x * 10 + c - '0', c = NC()); x *= b;
}
int a[maxn];


unsigned int Find_first_bit1(int num)
{
int indexbit=0;
while( ((num&1)==0) && (indexbit< 8*sizeof(int)) )
{
num>>=1;
++indexbit;
}
return indexbit;
}

bool is_bit1(int num,unsigned int indexbit)
{
num = num >>indexbit;
return (num & 1 );
}

void Find_nums_appear_once(int a[],int n)
{
if(a== NULL || n< 2) return;
int ret=0;
for(int i=0; i<n; ++i)
ret^=a[i];
unsigned int indexof1 = Find_first_bit1(ret);
int num1 =0,num2 =0;
for(int j=0; j<n; ++j)
{
if(is_bit1(a[j],indexof1)) num1^=a[j];
else num2^=a[j];
}
printf("%d %d\n",num1<num2?num1:num2,num1>num2?num1:num2);
}
int main()
{
// freopen("in.txt","r",stdin);
ios::sync_with_stdio(false);
int n;
while(cin>>n)
{
for(int i=0; i<n; ++i) cin>>a[i];
Find_nums_appear_once(a,n);
}
return 0;
}

/**************************************************************
Problem: 1351
User: herongwei
Language: C++
Result: Accepted
Time:710 ms
Memory:5420 kb
****************************************************************/

is_bit1:作用是判断在num二进制表示中从右数起的indexbit为是否为1。
Find_first_bit1:在整数num的二进制表示中找到最右边是1的位。
Find_nums_appear_once:RT。