LeetCode 👉 HOT 100 👉 只出现一次的数字 - 简单题

644 阅读3分钟

「这是我参与2022首次更文挑战的第21天,活动详情查看:2022首次更文挑战」。

题目

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

说明:

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

示例1

输入: [2,2,1]

输出: 1

示例2

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

输出: 4

思路

如果不考虑时间和空间复杂度,首先我们可以统计 nums 中每个数字出现的次数,然后返回出现次数等于1的那个,即为答案;

    /**
     * @param {number[]} nums
     * @return {number}
     */
    var singleNumber = function(nums) {
        let map = {};

        // 统计次数
        for(let num of nums) {
            if(!map[num]) map[num] = 0;

            map[num]++;
        }

        // 寻找次数为1的,并返回
        for(let num in map) {
            if(map[num] == 1) return num;
        }
    };

思考: 上述的解法时间、空间复杂度都为 O(n),且在时间复杂度上,进行了两次 O(n) 遍历,因为在后面还进行了一个寻找次数为1的元素 上

为了简化后面寻找次数为1的元素所花费的时间,在存储次数的时候,可以选择使用 Set 结构,遍历时,判断 Set 中是否存在该元素,不存在则放入 Set 中,存在则删除该元素;最后 Set 中就会只剩下一个元素,即为题目所求;

    /**
     * @param {number[]} nums
     * @return {number}
     */
    var singleNumber = function(nums) {
        let set = new Set();

        for(let num of nums) {
            if(!set.has(num)) {
                set.add(num);
            } else {
                set.delete(num);
            }
        }

        // 转换为 Array结构
        set = [...set];

        return set[0]
    };

再思考: 上面两种算法的空间复杂度都为 O(n),不满足题目要求不使用额外空间的条件。

这里如果想到满足题目要求,就要用到位运算的 异或 操作了,因为该运算有如下特性:

  • 一个数和0做异或运算,等于它本身
  • 一个数和自身做异或元素,等于0
  • 且该运算满足交换率和结合律

例如:

nums = [2, 2, 1]  => 2 ^ 2 ^ 1 => 0 ^ 1 = 1

nums = [2, 1, 2]  => 2 ^ 1 ^ 2 => 2 ^ 2 ^ 1 = 1

所以只需要将数组中的每个数,进行 异或 操作即可得出答案,且空间复杂度为 O(1)

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

        for(let num of nums) {
            ans ^= num;
        }

        return ans
    };

小结

位运算在平时的工作中,使用到的场景较少,但是在一些特殊场景中,它的运算效率非常高;如果没记错的话,React 的源码中,在计算任务的优先级 lane 的时候,就用上了位运算哦~

LeetCode 👉 HOT 100 👉 只出现一次的数字 - 简单题

合集:LeetCode 👉 HOT 100,有空就会更新,大家多多支持,点个赞👍

如果大家有好的解法,或者发现本文理解不对的地方,欢迎留言评论 😄