剑指Offer 数组中只出现一次的两个数字
程序员文章站
2022-03-08 16:23:58
...
题目描述:一个整数数组除出了两个数字外,其他数字都出现了两次,请写出程序找出这两个只出现了一次的数字,并将两个数字分别放入num1[0]、num2[0]中。
看到这道题我立刻想到了之前做过的一道找出数组中只出现一次的数字,要用到异或的逻辑运算,因为任何一个数字异或它自己都为0,那么只要对数组中每个元素异或一遍剩下的就是只出现一次的数字。我想这道题一定也是要用到这种方法来解决。但是这道题有两个数字出现了一次,如果只是简单地对数组中各个元素进行异或操作,那么我们最后得到的结果便是应该得到的两个数字的异或结果,因此本题要思考的就是如何将两个只出现一次的数字分到两个子数组中,这样分别对两个子数组进行遍历异或操作,那么就能找到两个只出现一次的数。
那么要基于什么样的规则将给定数组分为两部分,每一部分都包括只出现一次的数字?
我们想一下,既然两个数字在数组中都分别只出现了一次,那么显然这两个数异或出的值不会为0,因此我们可以找到两个数异或结果的第一个1位记下它的位置,显然在这个位置上,两个数必其中一个为1位,令一个为0位,因此我们可以根据每个数字的相应位置是不是1位来讲数组分开。这样就可以解决这道题。
//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
public class Solution {
private static int FindFirstone(int res){
int indexBit = 0;
while((res & 1) == 0){
res = res >> 1;
indexBit++;
}
return indexBit;
}
private static boolean isBit1(int num, int indexOf1){
num = num >> indexOf1;
return (num & 1)==1;
}
public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
// 首先对数组中每个元素异或操作,这样的话得到的就是两个只出现一次的数字的异或结果
int len = array.length;
int res1 = 0;
if(array == null || len < 2)
return;
for (int i = 0; i < len; i++){
res1 ^= array[i];
}
// 找到re1中第一个1位
int indexOf1 = FindFirstone(res1);
// 将数组从这个位置分来,这样就能得到两个数组分别包含两个只出现一次的数字
for(int i = 0; i < len; i++){
if (isBit1(array[i], indexOf1))
num1[0] ^= array[i];
else
num2[0] ^= array[i];
}
}
}