LeetCode探索(二):136_只出现一次的数字

144 阅读2分钟

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

题目

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

说明:

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

示例 1:

输入: [2,2,1]
输出: 1

示例 2:

输入: [4,1,2,1,2]
输出: 4

思考

如果不考虑时间复杂度和空间复杂度的限制,可以有多种解法:

  • 使用哈希表存储每个数字和该数字出现的次数。
  • 使用集合存储数字。第一次遍历时加入集合,第二次遍历时从集合中删除该元素。
  • 利用集合去重、求和,再与去重前的求和结果做数学运算。

以上方法都需要使用 O(n) 的空间复杂度,其中 n 是数组长度。

如何做优化,使得解法能做到线性时间复杂度和常数空间复杂度呢?

答案是使用位运算,借助异或运算去优化解法。

异或运算有以下三个性质:

  • 任何数和 0 做异或运算,结果仍然是原来的数。
  • 任何数和其自身做异或运算,结果是 0。
  • 异或运算满足交换律和结合律。

那么,对于数组nums,对每个元素依次进行异或运算,由于出现两次的数进行异或运算得到0,因此最终结果是只出现一次的数字,也就是方法二的解法

位运算的解法看上去是优雅了很多!

解答

方法一:哈希表

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 [num, val] of map.entries()) {
    if(val === 1) {
      ans = num
    }
  }
  return ans;
};

复杂度分析

  • 时间复杂度:O(n),其中 n 是数组 nums 的长度。
  • 空间复杂度:O(n),即为哈希映射需要使用的空间。

方法二:位运算

var singleNumber = function(nums) {
  let result = 0;
  for(let i = 0; i < nums.length; i++){
    result ^= nums[i];
  }
  return result;
};

复杂度分析

  • 时间复杂度:O(n),其中 n 是数组 nums 的长度。只需要对数组遍历一次。
  • 空间复杂度:O(1)。