Leetcode-260. 只出现一次的数字 III

1,103 阅读2分钟

题目:

给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。你可以按 任意顺序 返回答案。
进阶:你的算法应该具有线性时间复杂度。你能否仅使用常数空间复杂度来实现?

示例 1:

输入: nums = [1,2,1,3,2,5]
输出: [3,5]
解释: [5, 3] 也是有效的答案。

示例 2:

输入: nums = [-1,0]
输出: [-1,0]

示例 3:

输入: nums = [0,1]
输出: [1,0]

来源:力扣(LeetCode)
链接:leetcode-cn.com/problems/si…
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

结题思路

  1. 当一个数组只有一个只出现一次的数字 异或:相同位异或结果为0,不同位异或结果为1
0 ^ 1 = 1
1 ^ 1 = 0

如果一个数组[1,1,2,2,3,3,4]数组都出现两次,只有一个出现一次,那么将数组各个数字异或,出现两次的数字异或的结果为0,最后0异或4还是为4,就将只出现一次的数字找出来了。

  1. 当数组中有两个只出现一次的数字 对于题目的情况,有两个只出现了一次的数字ab,那么整个数组异或的结果就是这两个数字异或的结果a^b
    例如[1,2,1,3,2,5]整个数组异或的结果就是3^5结果为0011^0101=0110,言下之意就是整个数组有两个只出现了一次的数字,这两个数子二进制的倒数第二或者倒数第三位不一样,那么就可以通过倒数第二或者倒数第三位来区分这两个数。

将整个数组异或的结果,从右往左找到第一个为1的位,这个位可以将整个数组分为两组,35会分别分到不同的组,这样将问题转化为两个1小结所述的情况。分别对这两组进行异或就会获得两个只出现一次的数字。

具体算法

public int[] singleNumber(int[] nums) {
    // 记录组A的异或结果
    int a = 0;
    // 记录组B的异或结果
    int b = 0;
    // 记录异或的结果
    int xor = 0;

    // 获取整个数组的异或值,相同的数字就为0,得到的是所需两个值的异或
    for (int i = 0; i < nums.length; i++) {
        xor ^= nums[i];
    }

    // 对xor从右往左找第一个为1的位
    int div = 1;
    while ((xor & div) == 0) {
        div = div << 1;
    }

    // 遍历这个数组,使用div将数组分为两组,并分别异或两组,得到两个只出现一次的值
    for (int i = 0; i < nums.length; i++) {
        if ((div & nums[i]) == 0) {
            a = a ^ nums[i];
        } else {
            b = b ^ nums[i];
        }
    }

    // 返回a和b
    return new int[]{a, b};
}