您的位置:首页 > Web前端

剑指Offer:数组中只出现一次的数字

2018-02-26 23:20 155 查看
一个整型数组中除了两个数字之外,其他数字都是出现两次。请写程序这两个只出现一次的数字。要求时间复杂度为O(n),空间复杂度为O(1)。

例如数组{2,4,3,6,3,2,5,5},因为只有数字4和6这两个数字只出现了一次,其他数字都出现了两次,所以输出4和6。

数组中只有一个数字是单独的,怎么解决?

异或运算的一个性质:两个相同的数字进行异或运算结果为0。也就是说我们从头到尾异或每一个数字最终的结果就是单独出现的那个数字。

实现代码:

private static void FindNumAppearOnce(int[] arr){
if(arr==null||arr.length==0){
throw new RuntimeException("Array is null or length==0");
}
int t=0;
for(int i=0;i<arr.length;i++){
t ^= arr[i];
}
System.out.println("只出现一次的数字:"+t);
}


拆分数组,单独使用上述方法

一个数组中只有一个数字是只出现一次的问题我们已经解决了,可是题目中有两个数字只出现一次,所以我们是否可以将原数组拆分为两个数组,两个只出现一次的数字分别位于这两个数组中,这样这个问题就退化为了上面所述的问题了。

我们还是从头到尾依次异或数组中的每一个数字,那么最终得到的结果就是两个只出现一次的数字的异或结果。因为其他数字都出现了两次, 在异或中全部抵消了。由于这两个数字肯定不一样,那么异或的结果肯定不为 0,也就是说在这个结果数字的二进制表示中至少就有一位为1。我们 在结果数字中找到第一个为1 的位的位置 ,记为第n 位。现在我们以第 n 位是不是1为标准把原数组中的数字分成两个子数组,第一个子数组中每个数字的第 n位都是 1,而第二个子数组中每个数字的第 n位都是 0。由于 我们分组的标准是数字中的某一位是1还是 0,那么出现了两次的数字肯定被分配到同一个子数组。因为两个相同的数字的任意一位都是相同的,我们不可能把两个相同的数字分配到两个子数组中去,于是我们已经把原数组分成了两个子数组,每个子数组都包含一个只出现一次的数字,而其他数字都出现了两次。

实现代码:

private static int[] FindNumsAppearOnce(int[] arr){
if(arr==null||arr.length==0){
throw new RuntimeException("Array is null or length==0");
}
int t=0;
for(int i=0;i<arr.length;i++){
t ^= arr[i];
}
//每个数字进行异或,获得结果,找出二进制中最右侧1的位置
int indexOf1 = FirstBit1FromRight(t);

int num1=0,num2=0;
for(int i=0;i<arr.length;i++){
if(IsBit1(arr[i], indexOf1)){//按照最右侧n位是否为1进行拆分
num1 ^= arr[i];//在每个“数组”中使用上面的方法
}else{
num2 ^= arr[i];
}
}
return new int[]{num1,num2};
}

//将数组中每个数字异或,找出结果二进制中最右侧的1的位置
private static int FirstBit1FromRight(int num){
int index=0;
while((num&1)==0&&index<32){
num = num>>1;
index++;
}
return index;
}

//判断数字的第n位是不是1
private static boolean IsBit1(int num,int n){
num = num>>n;
return (num&1)==1;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息