参考自:《剑指Offer——名企面试官精讲典型编程题》
题目:数组中只出现一次的两个数字
一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
主要思路:根据异或运算的性质:数字异或它自身等于0。若数组中只有一个数字出现一次 ,其他数字都出现2次,那么从头到尾异或数组每个的数字,则最后结果就是只出现一次的数字。
相同的思路,若能把当前题目中的数组分成两个子数组,使得每个子数组都包含一个只出现一个的数字,其他数字都成对出现,则分别对两个子数组所有数字进行异或运算,就可以得到所求两个数。拆分原数组过程:异或原数组中的所有数字,得到一个非零数字a(即为两个只出现一次的数字的异或结果,其他数字都抵消了),从右向左找出a中第一个为1的位数,所求两个数在该位数上肯定不一样(位数异或为1,说明位数的值不相同,),以该位数是否为1作为划分标准,把原数组拆分成2个子数组,则所求两个数分别在这两个子数组中。
关键点:异或运算的特点,数组拆分
时间复杂度:O(n)
public class NumbersAppearOnce
{
public static void main(String[] args)
{
int[] data = {2, 4, 3, 6, 3, 2, 5, 5};
int[] num1 = new int[1];
int[] num2 = new int[1];
boolean canFind = findTwoNumbersAppearOnce(data, num1, num2);
if (canFind)
{
//结果:6, 4
System.out.println(num1[0]);
System.out.println(num2[0]);
}
}
//查找数组中只出现一次的两个数字
private static boolean findTwoNumbersAppearOnce(int[] array, int num1[], int num2[])
{
if (array == null || array.length < 2) return false;
int exorResult = 0;
for (int num : array)
{
exorResult ^= num;
}
//从右向左找到第一个为1的位的位置
int firstOneIndex = findFirstOneBit(exorResult);
int result1 = 0;
int result2 = 0;
for (int num : array)
{
//分成两个子数组
if (isOneBitAt(firstOneIndex, num))
{
result1 ^= num;
} else
{
result2 ^= num;
}
}
num1[0] = result1;
num2[0] = result2;
return true;
}
/**
* 从右向左找到第一个为1的位的位置
*
* @param num
* @return
*/
private static int findFirstOneBit(int num)
{
int oneBitIndex = 0;
while ((num & 1) == 0 && oneBitIndex < 32)
{
num = num >>> 1;
oneBitIndex++;
}
return oneBitIndex;
}
/**
* bitIndex处的位数是否为1
*
* @param bitIndex
* @param num
* @return
*/
private static boolean isOneBitAt(int bitIndex, int num)
{
num = num >> bitIndex;
return (num & 1) == 1;
}
}