要求:一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
异或性质:
1、两个数字异或的结果a ^ b是将 a 和 b 的二进制每一位进行运算,得出的数字。 运算的逻辑是如果同一位的数字相同则为 0,不同则为 1。
2、任何数和本身异或则为 0
3、任何数和 0 异或是 本身
4、异或满足交换律。 即 a ^ b ^ c ,等价于 a ^ c ^ b
思路: 这一题如果对全员进行异或的话,由于数组中存在着两个数字不重复的情况,我们将所有的数字异或操作起来,最终得到的结果是这两个数字的异或结果:(相同的两个数字相互异或,值为0)) 最后结果一定不为0,因为有两个数字不重复。
因此,如果我们可以把所有数字分成两组,使得:
1)两个只出现一次的数字在不同的组中;
2)相同的数字会被分到相同的组中。
那么对两个组分别进行异或操作,即可得到答案的两个数字。
通过 & 运算来判断一位数字不同即可分为两组,这样那么我们随便两个不同的数字至少也有一位不同!
public int[] singleNumbers(int[] nums) {
int sum=0;
//将数组所有元素进行异或,最后的结果一定是那两个不同数字的异或结果。
//用示例[4,4,6,1]最后的抑或结果就是 6和1异或的结果 7
for (int i = 0; i <nums.length ; i++) {
sum^=nums[i];
}
int first = 1;
// 两个不同数字进行异或的结果为sum,由于位中相同值的异或结果为0(1^1=0),为了使两个只出现一次的数字在不同的组中;所有需要找到sum中数值不同的那一位,也就是sum&first!=0的first,作为分组依据。
//通过与运算找到result第一个不为0的首位,7=>0111,也就是第一位,第二位和第三位也能作为
while((sum&first)==0){
first=first<<1;
}
//first为1,所以我们可以根据数组元素的二进制低位第一位是否为1,将数组分为2类,
// 示例数组可以分为 低位第一位为0:[4,4,6] 低位第一位为1:[1]
//此时再将两个数组两两异或就可以得到最终结果。
int result[]=new int[2];
for(int i=0;i<nums.length;i++){
//将数组分类。
if((nums[i]&first)==0){
result[0]^=nums[i];
}
else{
result[1]^=nums[i];
}
}
return result;
}
网友评论