题目一:一个数组里除了一个数之外,其他的数都出现了两次,请找出这个数。
思想:题目里强调, 一个数字出现了一次,其余的都出现了两次。我们想到异或的一个性质:任何一个数字异或自身等于0.也就是说,如果我们从头到尾异或数组中的每一个数字,那么结果为只出现一次的数字,因为出现两次的数字全部抵消了。
void FindNumberAppearOnce(int data[] ,int length ,int *num)
{
int i;
*num=0;
for(i=0;i<length;i++)
*num^=data[i];
}
题目二:如果数组里有两个数字只出现了一次呢?
思想:根据上面的思想,我们试着把原数组分成两个数组,使得每个数组符合题目一的性质,如果能这样拆分,我们就可以按照题目一的方法找到这两个数。
我们还是从头到尾异或数组中的每一个数字,最终得到的是两个只出现一次的数字的异或结果。因为他们不同,所以异或结果一定不等于0,我们在结果里找到第一个为1的位的位置,根据这个位是否为1我们将数组分为两组。 这两个数组都符合题目一的性质。
void FindNumberAppearOnce(int data[],int length ,int *num1,int *num2)
{
if(data == UNLL || length <2)
return ;
int resultOfOR=0;
for(int i=0;i<length;i++)
resultOfOr ^=data[i];
int indexOf1=FindFirstBitIs1(resultOfOr);
*num1=*num2=0;
for(int j=0;j<length ++j)
{
if(IsBit1(data[j],indexOf1))
*num1^=data[j];
else
*num2^=data[j];
}
}
unsigned int FindFirstBitIs1(int num)
{
int indexBit = 0;
while( (indexBit<8*sizeof(int)) && (num&1==0))
{
num>>1;
++indexBit;
}
return indexBit;
}
bool IsBit1(int num, unsigned int indexBit)
{
num = num>>indexBit;
return(num&1);
}
题目三:如果三个数只出现了一次呢?
思想:
比如,1,2,3,1,2,3,4,5,6。在这个数组中,456出现了一次,123出现了两次。那么我们的目的就是求出456。
首先我们可以想到的是,这个数组绝对是有奇数个元素的。
那么我们可以根据不同的bit的值【不是1就是0】,将数组分成两个数组。第一个数组该bit为1,记该数组的元素个数为count1,第二个该bit为0,记该数组的元素个数为count0。
那么,先判断count1和count0哪个是奇数。
针对奇数的那个数组,比如说是count1,另外一个数组的的所有数字异或,结果可能为0【说明所有的三个只出现一次的数字都在count1的那个数组中】,也可能不为0【说明三个只出现一次的数字有两个在本数组中】。
如果第二个数组的异或结果不为0,那么第一个数组的所有值异或起来就是一个答案,记为a,再在第二个数组中找到那两个数字就可以了。
如果第二个数组的异或结果为0,那么三个数字都在第一个数组中。继续根据其他bit来划分。