面试题56:数组中只出现一次的两个数字
题目要求:
一个整数数组里除了两个数字出现一次,其他数字都出现两次。请找出这两个数字。要求时间复杂度为o(n),空间复杂度为o(1)。
解题思路:
这道题可以看成“数组中只出现一次的一个数字”的延伸。如果所有数字都出现两次,只有一个数字是出现1次,那么可以通过把所有所有进行异或运算解决。因为x^x = 0。
但如果有两个数字出现一次,能否转化成上述问题?依旧把所有数字异或,最终的结果就是那两个出现一次的数字a,b异或的结果。因为a,b不想等,因此结果肯定不为0,那么结果的二进制表示至少有一位为1,找到那个1的位置p,然后我们就可以根据第p位是否为1将所有的数字分成两堆,这样我们就把所有数字分成两部分,且每部分都是只包含一个只出现一次的数字、其他数字出现两次,从而将问题转化为最开始我们讨论的“数组中只出现一次的一个数字”问题。
实例分析(以2,4,3,6,3,2,5,5为例):
相关数字的二进制表示为:
2 = 0010 3 = 0011 4 = 0100
5 = 0101 6 = 0110
步骤1 全体异或:2^4^3^6^3^2^5^5 = 4^6 = 0010
步骤2 确定位置:对于0010,从右数的第二位为1,因此可以根据倒数第2位是否为1进行分组
步骤3 进行分组:分成[2,3,6,3,2]和[4,5,5]两组
步骤4 分组异或:2^3^6^3^2 = 6,4^5^5 = 4,因此结果为4,6。
代码实现:
package chapter6;
/**
* Created with IntelliJ IDEA
* Author: ryder
* Date : 2017/8/17
* Time : 10:58
* Description:数组中数字出现的次数
* 有两个数字分别出现一次,其他的都出现两次,找到这两个数字
**/
public class P275_NumberAppearOnce {
public static int[] findNumsAppearOnce(int[] data){
int result = 0;
for(int i=0;i<data.length;i++)
result^=data[i];
int indexOf1 = findFirstBit1(result);
int ret[] = new int[]{0,0};
if(indexOf1<0)
return ret;
for(int i=0;i<data.length;i++){
if((data[i]&indexOf1)==0)
ret[0]^=data[i];
else
ret[1]^=data[i];
}
return ret;
}
public static int findFirstBit1(int num){
if(num<0)
return -1;
int indexOf1 = 1;
while (num!=0){
if((num&1)==1)
return indexOf1;
else{
num = num>>1;
indexOf1*=2;
}
}
return -1;
}
public static void main(String[] args){
int[] data = new int[]{2,4,3,6,3,2,5,5};
int[] result = findNumsAppearOnce(data); // 4,6
System.out.println(result[0]);
System.out.println(result[1]);
}
}
运行结果
4
6