Given an array of integers, every element appears three times except for one. Find that single one.
Note:
Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?
对于”只有一个数出现一次,其余数出现2次“的情况,很简单,只要把数组中所有数异或一遍就是这个出现一次的数。
本题用一个大小为32的数组cnt来记录:(int数据的每个二进制位1的出现次数)%3,程序中cnt[i]记录第i+1个二进制位。如果某个数出现了三次并且他的第i+1个二进制位为1,因为做了模3操作,那么该数对数组cnt[i]的贡献为0,那么遍历完整个输入数组后,cnt[i]存储的就是这个出现一次的数的第i+1个二进制位。剩余的位就是只出现一次的那个数了。不过还要考虑正负号。
对于数组中的负数,都取其绝对值,这对结果没有影响,只是cnt数组最后保存的是这个出现一次的数的绝对值的二进制位,因此同时还要通过记录原数据的符号位来确定这个只出现一次的数的正负。
#include<iostream>
#include<cstdio>
using namespace std;
#define N 1005
int singleNumber(int A[],int n)
{
int countOne[32]={0},i;//cut[i]表示A中第(i+1)个二进制位的1个数(结果对3求模)
int tmp,k;
int sign=0;//用来确定single number的符号,0正1负
for(i=0;i<n;i++)
{
tmp=A[i];
if(A[i]<0)//将负数变为正数
{
tmp*=-1;
sign=(sign+1)%3;//记录最高位为1还是为零
}
for(k=0;k<32;k++)//对全是正数进行处理
{
countOne[k]=(countOne[k]+(tmp&0x00000001))%3;
tmp=tmp>>1;
}
}
int res=0,base=1;
for(i=0;i<32;i++)
{
res+=(base*countOne[i]);//将二进制数变为整数
base<<=1;//权重
}
return sign==0?res:-1*res;//若为负数,则恢复为负数
}
int main(int argc,char *argv[])
{
freopen("input.txt","r",stdin);
int num,tmp,a[N],i;
cin>>num;
for(i=0;i<num;i++)
{
cin>>tmp;
a[i]=tmp;
}
cout<<singleNumber(a,num)<<endl;
return 0;
}