题目
给定一个整数数组,其中正好两个元素只出现一次,所有其他元素恰好出现两次。查找仅出现一次的两个元素。(使用位运算实现)
思路
首先要先明白一个知识:
异或相同为0,不同为1
n ^ 0 = n;
n ^ n = 0;
我们可以据此,先将整个数组的元素依次进行一次异或操作,得到最终的异或值。(两个相同的数字进行异或之后,会得0,而0异或任何数字得到该数字的本身,所以这个数组最后的异或值会为那两个不重复数字的异或值)
int temp = 0;
for (int i = 0; i < nums.length; i++) {
temp ^= nums[i];
}
得到那两个恰好只出现一次的元素的异或值后,开始两个元素划分的准备工作。
temp为二者异或值,异或是不同为1,可根据此为切入点来进一步操作。我们可以将temp和temp的相反数进行与运算,得到其异或值为1的最低位。(取最低位是因为方便)
正数的原码、反码、补码一致;负数在计算机中用补码进行运算,即负数绝对值原码取反后再+1------注意:如果要负数的原码,需要将求得的补码视为原码,再求补码
一个数和它的相反数进行与运算,能得到恰好一位为1,其余全为0
//Integer.MIN_VALUE 如果整数是最小位 会发生溢出 所以这么写
int min = temp == Integer.MIN_VALUE ? temp : temp & (-temp);
而后开始进行元素划分:那两个元素和min进行与运算,一定是一个为0,一个为1(位运算均为1才为1,而min仅有一位为1,min为1的那一位是两个元素异或为1的最低位,即两个元素不相等,所以与运算所得的值也是为0、为1,所以可以据此进行划分)
再用两个从0开始的数对这两个分类分别进行异或运算,因为相同的元素肯定会被分到同一个组,进行异或运算后会消除,最后每个组都只剩下那个唯一的数。
int num1 = 0;
int num2 = 0;
for (int i = 0; i < nums.length; i++) {
//为1的那位不同的那一组(即min那一位为1 这一组数字的那一位都为0)
if ((min & nums[i]) == 0) {
num1 ^= nums[i];
} else {//相同的那一组
num2 ^= nums[i];
}
}
整体代码展示
public class Test3CSDN {
public static void main(String[] args) {
int[] nums = {12, 34, 21, 34, 12, 22, 6, 18, 21, 6, 22, 17};
//首先找到在数组中不重复出现的那两个数字,求它俩的异或值
int temp = 0;
for (int i = 0; i < nums.length; i++) {
temp ^= nums[i];
}
//将二者的异或值与其负数进行与运算,可得二者出现1的最低位
int min = temp & (-temp);
int num1 = 0;
int num2 = 0;
for (int i = 0; i < nums.length; i++) {
//进行分组 根据min的那一位1进行划分
if ((min & nums[i]) == 0) {
num1 ^= nums[i];
}else {
num2 ^= nums[i];
}
}
System.out.println("仅出现一次的两个元素为:" + num1 + "," + num2);
}
}