LeetCode探索(三):137_只出现一次的数字II

709 阅读1分钟

这是我参与11月更文挑战的第 10 天,活动详情查看:2021最后一次更文挑战

题目

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

示例 1:

输入:nums = [2,2,3,2]
输出:3

示例 2:

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

提示:

  • 1 <= nums.length <= 3 * 10^4
  • -2^31 <= nums[i] <= 2^31 - 1
  • nums 中,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次

进阶:你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

思考

这道题是136_只出现一次的数字的升级版。

首先,我们可以想到的是使用哈希表,记录数组中每一个元素出现的次数。然后使用for循环遍历哈希表,找出哈希表中只出现了一次的元素。该算法的时间复杂度和空间复杂度都为O(n)。

其次,我们可以借助位运算去优化算法的空间复杂度。考虑到数组元素出现了三次,那么,对于数组元素,其二进制形式的每一位,出现1的次数会是3的倍数。基于此,求余之后,得到的便是数组中只出现了一次的元素。例子:

例子:nums = [3,3,5,3]
 11
 11
101
 11
数组元素的每一位,出现1的次数分别为134, 对3求余:
134 % 3 = 101 -> 5

该方法很巧妙!

解答

方法一:哈希表

var singleNumber = function(nums) {
  let map = new Map();
  let ans = 0;
  for (const num of nums) {
    map.set(num, (map.get(num) || 0) + 1);
  }
  for (const [key, value] of map.entries()) {
    if (value === 1) {
      ans = key;
    }
  }
  return ans;
};
  • 时间复杂度:O(n),其中 n 是数组 nums 的长度。
  • 空间复杂度:O(n)。

方法二:依次确定每一个二进制位

var singleNumber = function (nums) {
  let ans = 0;
  for (let i = 0; i < 32; i++) { // 数组元素在 int(即 32 位整数)范围内
    let temp = 0;
    for (const num of nums) {
      // 通过移位取出每个元素的最后一位,计算nums中最后一位为1的个数
      temp += (num >> i) & 1;
    }
    if (temp % 3 != 0) {
      // 左移至原位,按位或并赋值
      ans |= (1 << i);
    }
  }
  return ans;
};
  • 时间复杂度:O(n),其中 n 是数组 nums 的长度。
  • 空间复杂度:O(1)。

注释:(num >> i) & 1

当 num = 3,i = 0 时,
num >> i === 11 >> 0 === 11
11 & 1 === 1 // 得到 num 的第 i 个二进制位