代码来自http://blog.youkuaiyun.com/ns_code/article/details/27800577
总结如下
整个大的问题中含有三个问题
问题1:整个数据中所有元素出现两次除了1个元素出现一次
这个简单,代码如下,所有元素异或即可。只剩下最后只出现一次的数
void FindOnce(int arr[],int length,int* num)
{
if(arr==NULL)
return;
*num=0;
for(int i=0; i<length; i++)
{
*num ^= arr[i];
}
}
问题2:整个数据中所有元素出现两次除了2个元素出现一次
方案剑指Offer中有,将所有的数异或后,最后边一位不同的0,1将整个数组分成2分,刚好每个小的数据中仅有1个数出现1次
/*
返回num的最低位的1,其他各位都为0
*/
int FindFirstBit1(int num)
{
//二者与后得到的数,将num最右边的1保留下来,其他位的全部置为了0
return num & (-num);
}
bool IsBit1(int data,int res)
{
return ((data&res)==0) ? false:true;
}
void FindTwoNumsAppearOnce(int *arr,int len,int *num1,int *num2)
{
int i;
int AllXOR = 0;
//全部异或
for(i=0;i<len;i++)
AllXOR ^= arr[i];
int res = FindFirstBit1(AllXOR);
*num1 = *num2 = 0;
for(i=0;i<len;i++)
{
if(IsBit1(arr[i],res))
*num1 ^= arr[i];
else
*num2 ^= arr[i];
}
}
问题3:整个数据中所有元素出现两次除了3个元素出现一次
先找到3个不同数的一个,然后采用问题2的解法即可
关键就是如何将数组分成2个只出现一次的数和一个只出现一次的数
由于有3个数只出现一次,这三个数中肯定至少有一位某一位上数对应是011,或者100,即有存在某一位上可以将3个只出现1次的数分成2份,其中一份中仅有1个1次出现的数
其中temp0,temp1分别为某一位上为0,1的数异或结果。存在如下情况
temp1为1,则这一位上3个数只可能为1,1,1或者1,0,0
如果是前一种情况temp0为0,继续找下一位数
如果是后一种情况temp0不为0,这时候temp1的结果即为所求
同理
temp1为0,这一位上3个只出现一次的数可能为1,1,0或者0,0,0
如果前一种情况temp1不为0,返回temp0即可。
如果是后一种情况temp1为0,继续找下一个位
/*
通过扫面每一位,先找出一个只出现一次的数
*/
int FindOneNumAppearOnce(int *arr,int len)
{
int count1 = 0; //某一位上1的个数
int count0 = 0; //某一位上0的个数
int temp1 = 0; //某一位为1的所有数相异或的结果
int temp0 = 0; //某一位为0的所有数相异或的结果
int i,j;
for(i=0;i<8*sizeof(int);i++) //循环计算每一位的以上四个数据
{
count1 = count0 = temp1 = temp0 = 0;//每次计算下一位时清零
for(j=0;j<len;j++)
{
//每次向左移一位进行计算
if(arr[j] & (1<<i)) //该位为1时
{
temp1 ^= arr[j];
count1++;
}
else
{
temp0 ^= arr[j];
count0++;
}
}
if(temp1 & (1<<i)) //某位上有奇数个1 (1<<i)
{
if(temp0 == 0) //此时3个不同数的该位都为1
continue;
else //此时3个不同数的该位有1个1,2个0
return temp1;
}
else //某位上有偶数个1
{
if(temp1 == 0) //此时3个不同数的该位都为0
continue;
else //此时3个不同数的该位有1个0,2个1
return temp0;
}
}
}
/*
返回num的最低位的1,其他各位都为0
*/
int FindFirstBit1(int num)
{
//二者与后得到的数,将num最右边的1保留下来,其他位的全部置为了0
return num & (-num);
}
bool IsBit1(int data,int res)
{
return ((data&res)==0) ? false:true;
}
void FindTwoNumsAppearOnce(int *arr,int len,int *num1,int *num2)
{
int i;
int AllXOR = 0;
//全部异或
for(i=0;i<len;i++)
AllXOR ^= arr[i];
int res = FindFirstBit1(AllXOR);
*num1 = *num2 = 0;
for(i=0;i<len;i++)
{
if(IsBit1(arr[i],res))
*num1 ^= arr[i];
else
*num2 ^= arr[i];
}
}
/*
交换两个int变量
*/
void Swap(int *a,int *b)
{
if(*a != *b)
{
*a ^= *b;
*b ^= *a;
*a ^= *b;
}
}
/*
找出这三个只出现一次的数字
*/
void FindThreeNumsAppearOnce(int *arr,int len,int *num1,int *num2,int *num3)
{
if(arr==NULL || len<3)
return;
*num1 = FindOneNumAppearOnce(arr,len);
//找到第一个找出的数字,并与最后一个元素交换,便于接下来剩下的两个数字
int i;
for(i=0;i<len;i++)
if(*num1 == arr[i])
break;
Swap(&arr[i],&arr[len-1]);
FindTwoNumsAppearOnce(arr,len-1,num2,num3);
}