题目:一个整型数组里除了两个数字之外,其他的数字都出现两次。请找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度O(1)。
看到这个问题,我们首先应该想到另一个问题,如果数组中只有一个数字出现一次,怎么找到它呢?
由此引出异或运算:任何一个数字异或它自己都等于0.
所以如果我们从头到尾依次异或数组中的每一个数字,那么最终的结果刚好是那个只出现一次的数字,其它成对出现的已经全部在异或中抵消了。
现在回到原问题,若把数组分成两个数组,每个数组包含一个只出现一次的数字,其它数字出现两次,然后分别在对两个数组用前面方法求异或,就可以得到正确结果,以下为代码实现:
#include <stdio.h> #include <stdlib.h> void FindNum_one(int arr[],int lenth,int *p1,int *p2) { int num=0;//用于存放数组数字依次异或后得到的结果 int bit=0;//用于判断num二进制最右边是1的位 int i=0; if((arr==NULL)&&(lenth<2))//若数组为空或长度为1则直接返回 { return; } for(i=0;i<lenth;i++)//从头到尾依次异或数组中数字 { num^=arr[i]; } while(((num&1)==0)&&(bit<32))//找出整数num二进制中表示最右边1的位 { num=num>>1; bit++; } for(i=0;i<lenth;i++)//划分数组并分别依次异或每个数组数据,得出结果*p1,*p2 { if((arr[i]>>bit)&1==1) { *p1^=arr[i]; } else { *p2^=arr[i]; } } } int main() { int arr[]={2,4,3,6,3,2,5,5}; int num1=0; int num2=0; int lenth=sizeof(arr)/sizeof(arr[0]); FindNum_one(arr,lenth,&num1,&num2); printf("两个只出现一次的数字是:\n%d %d\n",num1,num2); system("pause"); return 0; }
以上的过程就是对数组每一个数做异或运算后,得到结果0010,异或得到结果的倒数第二位为1,于是我们根据数字倒数第二位是不是1分成两个数组,第一个倒数第二位是1的数组{2,3,6,3,2},第二个倒数第二位为0的数组{4,5,5},然后分别异或,得出结果6,4.