剑指 Offer 56 - I. 数组中数字出现的次数
题目描述:
一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。
请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
示例1:
输入:nums = [4,1,4,6]
输出:[1,6] 或 [6,1]
示例2:
输入:nums = [1,2,10,4,1,4,3,3]
输出:[2,10] 或 [10,2]
public class JianZhiOffer56 {
//测试
public static void main(String[] args) {
int[] arr = new int[] {4,1,4,6};
int[] res = singleNumbers(arr);
for(int curr : res) {
System.out.println(curr);
}
}
// 剑指 Offer 56 - I. 数组中数字出现的次数
// 一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。
// 请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
// 异或运算: 0^N = N , N^N = 0 , (N^B)^C =N^(B^C) , N^A^N = N^N^A = A
public static int[] singleNumbers(int[] nums) {
// 返回的结果数组
int[] res = new int[2];
// N 异或 数组中的所有数
// 由题可知 ,只有两个数字出现一次,那么假设这两个只出现一次的数字为 x 和 y
// 最后异或出来的结果是 N = x^y
int N = 0;
for(int curr : nums) {
// N 异或 数组中的所有数
N ^= curr;
}
// 接下来的问题是如何将 x 和 y 分开, 可以根据 x != y ,那么 x 和 y 必定有一位不同,必定一个为 0,另一个为 1 ;
// 假设 x 与 y 不同的位置是z , 那么 N 在z位置上的数字必定为 1 ; 异或规则 : 相同为0,不同为1;
// 找到某个为1的位置z
// 得到最右侧的 1
// N 和 N取反加1 进行与运算
int z = N & (~N + 1) ;
// 另设一个变量K,对z位置上为0 或 为1 的所有数字进行异或运算 , 那么最后得到的一定是 x 或者y 中的某一个
// 因为 x 和 y 在 z位置不同, 所有不可能同时存在于 z位置上为0的所有数字中 ,也不可能同时存在于 z位置上为 1的所有数字中
int K = 0;
for(int curr : nums) {
// z位为0 的数
if((z & curr) == 0) {
//最后K = x 或者 K = y
K ^= curr;
}
}
res[0] = K ;
//假如 K = x , 又知 N = x ^ y ,那么 x ^ x ^ y = y ; K = y 同理
res[1] = K ^ N;
return res;
}
}