题目:一个整形数组里面,只有两个数字出现了一次,其他的数字都出现了两次,求只出现一次的数。
分析:首先考虑这个问题的一个简单版本,一个数组里除了一个数字外,其他的数字都出现了两次。请写程序找出只出现一次的数字?
这个问题的突破口在哪里?为什么别的数字都是出现了两次?这里就是为了让我们想到异或运算的性质:任何一个数字异或它自己都等于0,若没有这个特性,我们可以循环遍历数组,给每个出现的数记一次数,可以用hashmap的key,value实现。但是有了这个特性我们就要想到更高效的算法。即从头到为依次异或数组中的每一个数字,那么最终的结果刚好是那个只出现一次的数字,出现两次的数字全部在异或中抵消掉了。
有了上面这个简单的解决方案之后,回到原始问题。我们能不能把原数组分为两个子数组。每个子数组中,包含一个只出现一次的数字,而其他数字都出现了两次,如果能这样拆分原数组,按前面的办法就可以分别求出这两个只出现一次的数字。
所以,第一步还是从头到尾依次异或数组中的每一个数字,那么最终得到的结果就是两个只出现一次的数字的异或结果。因为其他数字都出现了两次,在异或中全部抵消掉了。第二步,由于这两个数字肯定不一样,那么异或结果肯定不为0,也就是说这个结果数字的二进制表示中至少有一位是1.,所以我们在异或结果数字中找到第一个1的位置,记为第N位。第三步,以这第N位为标准将数组分为两个部分。在二进制第N位为1的都放在一个数组,为0的放到另一个数组,这样两个子数组中就各包含一个只出现一次的数字,这边大家要想一下,因为是进行异或操作,所以在第N位,两个只出现一次的数字肯定不一样,有了这个异或的性质才能这样分组。
//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
public class Solution {
public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
if(array==null ||array.length<2)
return ;
int temp = 0;
//依次异或数组中的数,得到结果为两个只出现一次的数字的异或结果
for(int i=0;i<array.length;i++)
temp ^= array[i];
//获取第一个1在异或结果中出现的位置
int indexOf1 = findFirstBitIs(temp);
for(int i=0;i<array.length;i++){
//异或第一个数组得到第一个出现一次的值
if(isBit(array[i], indexOf1))
num1[0]^=array[i];
else
num2[0]^=array[i];
}
}
//获取第一个1在异或结果中出现的结果
public int findFirstBitIs(int num){ int indexBit = 0;
//num只要还不为0,就右移1为,计数Index+1,这样就可以得到第一个1出现的位置
while(((num & 1)==0) && (indexBit)<8*4){
num = num >> 1;
++indexBit;
}
return indexBit;
}
//判断第N为是否为1,作为分类标准
public boolean isBit(int num,int indexBit){
num = num >> indexBit;
return (num & 1) == 1;
}
}