方法1
如果一个数组中只有一个数字是不同的,那么将整个数组异或一遍,得到的就是那个不同的数;例如a[0] ^ a[2] ^ …^a[n] = a[m]- - - - a[m]就是那个不同的数;基于这个思想,可以先遍历数组,将每个数异或一遍,得到的数n就是那两个不同的数异或的结果;再找到n的最右边第一个1的位置index,那么不同的那两个数的index位置的数肯定不同,所以将整个数组分成两部分;时间复杂度为O(n),空间复杂度为O(1);
public static void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
int temp = 0;
int index = 0;
// 找到整个数组亦或一次最后的结果m
for(int i = 0; i < array.length; i++)
temp ^= array[i];
// 找到m最右边第一个1的位置
for( ;index < 32; index++)
if((temp & (1 << index)) != 0) break;
/**
* 如果array[i]的第index位为1,则与num1[0]异或,
* 否则与num2[0]异或,相同的两个数它们第index位的数字肯定相同,
* 所以两个相同的数肯定能与同一个num相异或
* */
for(int i = 0; i < array.length; i++) {
if((array[i] & (1 << index)) != 0) num1[0] ^= array[i];
else num2[0] ^= array[i];
}
}
方法2
用哈希表,解这个问题的时间复杂度为O(n),空间复杂度为O(1);
public static void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
Set<Integer> set = new HashSet<>();
for(int i = 0;i < array.length; i++) {
if(!set.add(array[i])) {
set.remove(array[i]);
}
}
Iterator<Integer> i = set.iterator();
num1[0] = i.next();
num2[0] = i.next();
}
延伸问题
如果一个数组中只有一个数出现一次,其他数字都出现三次,请找出这个数
public static int find1From3(int[] a){
int[] bits = new int[32];
int len = a.length;
/**
* 统计32位中,每一个位上面1的个数,如果一个数组{1,1,1}
* 那么数组第0位上的值是3,就是说有三个数的二进制第0位是1
* */
for(int i = 0; i < len; i++){
for(int j = 0; j < 32; j++){
bits[j] = bits[j] + ( (a[i]>>>j) & 1);
}
}
/**
*找出数组中不能被3整除的那个位,则要找的那个数在这个位上肯定是1
* */
int res = 0;
for(int i = 0; i < 32; i++){
if(bits[i] % 3 !=0){
res = res | (1 << i);
}
}
return res;
}