【图解乱杀算法】136.只出现一次的数字

106 阅读2分钟

题目

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

你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。

示例 1 :

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

示例 2 :

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

示例 3 :

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

解法一:hash

很自然地会想到用hash表来解题,先上hash的方法(我写得不是很优雅)

/**
 * @param {number[]} nums
 * @return {number}
 */
var singleNumber = function(nums) {
    let map = new Map();
    nums.forEach(el => {
        if (!map.has(el)) {
            map.set(el, 1);
        } else {
            map.set(el, map.get(el) + 1);
        }
    })
    for(const [key, value] of map) {
        if (value === 1) {
            return key;
        }
    }
};

其中还遇到了一些坑,我在map.set(el, map.get(el) + 1)这一步原来是写成map.set(el, map.get(el)++)报错了。

为什么呢?

在 JavaScript 中,++ 运算符用于递增一个变量的值,并将递增后的结果赋值给该变量。

然而,map.get(element) 返回的是一个值,而不是一个变量,因此不能直接使用 ++ 运算符进行递增操作。

解法二:异或

第二种方法是看leetcode上别人的解法,用异或运算,顺便学一学优雅的解法。

首先得先知道异或运算的规则。

异或运算是一种逻辑运算,通常用符号 "XOR" 表示。

在布尔代数中,异或运算的结果为真(1),当且仅当操作数中恰好有一个为真时。换句话说,如果两个操作数相同则结果为假(0),如果两个操作数不同则结果为真(1)。

具体来说,如果将两个操作数记为 A 和 B,则异或运算的真值表如下:

  • A XOR B = 0 (false) 当 A = 0 且 B = 0,或者 A = 1 且 B = 1
  • A XOR B = 1 (true) 当 A = 0 且 B = 1,或者 A = 1 且 B = 0

这是异或运算的基本特性

但解这道题的关键不在于这个,而是在于下面这些隐藏规则:

  • 一个数和 0 做 XOR 运算等于本身:a⊕0 = a
  • 一个数和其本身做 XOR 运算等于 0:a⊕a = 0
  • XOR 运算满足交换律和结合律:a⊕b⊕a = (a⊕a)⊕b = 0⊕b = b

所以这题的解为

/**
 * @param {number[]} nums
 * @return {number}
 */
var singleNumber = function(nums) {
    let ans = 0;
    for(const num of nums) {
        ans ^= num;
    }
    return ans;
};