哈希表,位运算:找出只出现一次的数字

131 阅读2分钟

给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。 请你找出并返回那个只出现了一次的元素。

示例 1:
输入:nums = [2,2,3,2]
输出:3

示例 2:
输入:nums = [0,1,0,1,0,1,100]
输出:100

看到这样的需求和题目最先想到的方法就是哈希表的方法,将数组遍历,使用哈希表 key 保存出现的数字,value 保存出现的次数,默认为1,当再次出现时,value + 1,最终找到哈希表中 value = 1 的 key 即为只出现一次的数字。

代码实现:

/**
 * @param {number[]} nums
 * @return {number}
 */
var singleNumber = function(nums) {
    let map = {}
    nums.forEach(n => {
        let count = map[n] ? map[n] : 0
        count++
        map[n] = count
    })
    let res = ''
    Object.keys(map).forEach(key => {
        if (map[key] === 1) {
            res = key
            break
        }
    })
    return res
};

这是最简单有效的,也是最容易想到的解决方法,但如果我们不能新增不必要的空间,应该如何去解决这个问题??

以下转载位运算解决方案:

方法二:依次确定每一个二进制位 思路与算法

为了方便叙述,我们称「只出现了一次的元素」为「答案」。

由于数组中的元素都在 int(即 32 位整数)范围内,因此我们可以依次计算答案的每一个二进制位是 0 还是 1。

具体地,考虑答案的第 i 个二进制位(i 从 0 开始编号),它可能为 0 或 1。对于数组中非答案的元素,每一个元素都出现了 3 次,对应着第 i 个二进制位的 3 个 0 或 3 个 1,无论是哪一种情况,它们的和都是 3 的倍数(即和为 0 或 3)。因此:

答案的第 i 个二进制位就是数组中所有元素的第 i 个二进制位之和除以 3 的余数。

这样一来,对于数组中的每一个元素 x,我们使用位运算(x >> i) & 1 得到 x 的第 i 个二进制位,并将它们相加再对 3 取余,得到的结果一定为 0 或 1,即为答案的第 i 个二进制位。

代码:

var singleNumber = function(nums) {
    let ans = 0;
    for (let i = 0; i < 32; ++i) {
        let total = 0;
        for (const num of nums) {
            total += ((num >> i) & 1);
        }
        if (total % 3 != 0) {
            ans |= (1 << i);
        }
    }
    return ans;
};