1. 找出数组中只出现一次的数字
这个题目其实有两种形式:
-
一个整型数组里除了某一个数字之外,其他的数字都出现了两次,找出这个只出现了一次的数字
如数组 [1,2,2,1,3], 则目标数字为 3
-
一个整型数组里除了某两个数字之外,其他的数字都出现了两次,找出这两个只出现了一次的数字
如数组 [1,2,2,1,3,5], 则目标数字为 3,5
2. 解法
2.1 只有一个不重复数字
思路:使用异或运算符^,0与其他数字异或的结果是那个数字,相等的数字异或得 0
- 数组中除了某一个数字只出现了一次之外,其他数字都出现了两次,所以可定义一个变量赋初始值为0,用这个变量与数组中每个数字做异或运算,并将这个变量值更新为那个运算结果,直到数组遍历完毕,最后得到的变量的值就是数组中只出现了一次的那个数字
public static int findOnceFrom2(int[] a) {
int len = a.length, res = 0;
for (int i = 0; i < len; i++) {
res = res ^ a[i];
}
return res;
}
2.2 有两个不重复的数字
这个问题其实是基于以上问题的扩展,首先可知当只有一个数出现一次时,把数组中所有的数依次异或运算,最后剩下的就是落单的数,因为成对出现的都抵消了。
- 按照这个思路,首先还是先异或,
最后剩下的数字肯定是A、B异或的结果 C,以数组 [1,2,2,1,3,5]为例, 则 A=3(0011),B=5(0101),结果C=6(0110)
。C 的二进制表示中 1 所在的位,其实就是A 和 B 的二进制表示中数值不同的位
。我们取第一个1 所在的位数 index,可知是(0010 = 1<<index)即 index=1
,据此把原数组分成两组,分组标准是数字二进制表示的第2位是否为1。这样成对的数字肯定在一个组中,因为相同数字所有位的数值都相同,而不同的数肯定不在同一组。之后把这两个组按照最开始的思路,依次异或,剩下的两个结果就是这两个只出现一次的数字
public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
if(array == null || array.length <= 1){
num1[0] = num2[0] = 0; // 初始化为 0
return;
}
int len = array.length, index = 0, sum = 0;
for(int i = 0; i < len; i++){
sum ^= array[i];
}
for(index = 0; index < 32; index++){
if((sum & (1 << index)) != 0) break; // 获取第一个 1 所在位数 index
}
for(int i = 0; i < len; i++){
if((array[i] & (1 << index))!=0){ // 根据 index 将数组分为两部分
num2[0] ^= array[i];
}else{
num1[0] ^= array[i];
}
}
System.out.println(num1[0]+" "+num2[0]);
}