「LeetCode」268.丢失的数字

179 阅读2分钟

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

题目描述🌍

给定一个包含 [0, n]n 个数的数组 nums ,找出 [0, n] 这个范围内没有出现在数组中的那个数。

示例 1

输入:nums = [3,0,1]
输出:2
解释:n = 3,因为有 3 个数字,所以所有的数字都在范围 [0,3] 内。2 是丢失的数字,因为它没有出现在 nums 中。

示例 2

输入:nums = [0,1]
输出:2
解释:n = 2,因为有 2 个数字,所以所有的数字都在范围 [0,2] 内。2 是丢失的数字,因为它没有出现在 nums 中。

示例 3

输入:nums = [9,6,4,2,3,5,7,0,1]
输出:8
解释:n = 9,因为有 9 个数字,所以所有的数字都在范围 [0,9] 内。8 是丢失的数字,因为它没有出现在 nums 中。

示例 4

输入:nums = [0]
输出:1
解释:n = 1,因为有 1 个数字,所以所有的数字都在范围 [0,1] 内。1 是丢失的数字,因为它没有出现在 nums 中。

提示

  • n == nums.length
  • 1 <= n <= 104
  • 0 <= nums[i] <= n
  • nums 中的所有数字都 独一无二

进阶:你能否实现线性时间复杂度、仅使用额外常数空间的算法解决此问题?

排序📈

解题思路

数组排序后,当遍历数组元素不满足 i==nums[i] 则表明丢失的值为 i;若给定数组中的元素都满足 i==nums[i],则丢失的元素在最后一位,返回 nums.length

代码

Java

class Solution {
    public int missingNumber(int[] nums) {
        Arrays.sort(nums);
        int length = nums.length;
        for (int i = 0; i < length; i++) {
            if (nums[i] != i)
                return i;
        }
        return length;
    }
}

C++

class Solution {
public:
    int missingNumber(vector<int> &nums) {
        sort(nums.begin(), nums.end());
        int length = nums.size();
        for (int i = 0; i < length; ++i) {
            if (nums[i] != i)
                return i;
        }
        return length;
    }
};

时间复杂度:O(nlogn)O(n·logn)

空间复杂度:O(logn)O(logn)

哈希法🍤

解题思路

通过哈希集合可以有效降低时间复杂度。

遍历数组,将所有元素加入到哈希集合中,然后依次检查 0 到 n 哪个元素不在哈希集合中。

代码

Java

class Solution {
    public int missingNumber(int[] nums) {
        Set<Integer> hashSet = new HashSet<>();
        for (int num : nums) {
            hashSet.add(num);
        }
        int length = nums.length;
        for (int i = 0; i <= length; i++) {
            if (!hashSet.contains(i))
                return i;
        }
        return -1;
    }
}

C++

class Solution {
public:
    int missingNumber(vector<int> &nums) {
        unordered_set<int> hash;
        for (int num: nums) {
            hash.insert(num);
        }
        int length = nums.size();
        for (int i = 0; i < length; ++i) {
            if (hash.find(i) == hash.end())
                return i;
        }
        return length;
    }
};

时间复杂度:O(n)O(n)

空间复杂度:O(n)O(n)

差值法🚀

解题思路

既然是查找 0..n0..n 中丢失的数字,那么索性将 0..n0..n 所有数字进行累加,然后减去数组中的所有元素, 其差值就是所求的丢失数字。

🚄高斯公式求和:i=0n=n(n+1)2\sum_{i=0}^{n}=\frac{n(n+1)}{2}

代码

Java

class Solution {
    public int missingNumber(int[] nums) {
        int length = nums.length;
        int sum = accumulation(length);
        for (int i = 0; i < length; i++) {
            sum -= nums[i];
        }
        return sum;
    }
    
    // sum = n*(n+1)/2
    private int accumulation(int n) {
        return n * (n + 1) / 2;
    }
}

C++

class Solution {
public:
    int missingNumber(vector<int> &nums) {
        int sum = accumulation(nums.size());
        int missingSum = accumulate(nums.begin(), nums.end(), 0);
        return sum - missingSum;
    }

    int accumulation(int n) {
        return n * (n + 1) / 2;
    }
};

时间复杂度:O(n)O(n)

空间复杂度:O(1)O(1)

位运算「异或」🥇

解题思路

⭐异或运算规则:

  • x ^ 0 = x
  • x ^ x = 0
  • x ^ y ^ z = x ^ z ^ y

除了丢失的数字出现 0 次,其余数字皆出现了 1 次,那么我们可以再为它创造一个 0 到 n 的数组,这时丢失的数字出现 1 次,其余数字皆出现了 2 次。然后将这 2n+1 个元素进行异或运算,即可获得只出现 1 次的丢失数字了。

不过进阶要求空间复杂度为 O(1)O(1),那么我们用时间换空间,不创建数组了,直接逐一异或 0 到 n 的元素。

一图胜千言(举个栗子)

代码

Java

class Solution {
    public int missingNumber(int[] nums) {
        int x = 0;
        int length = nums.length;
        for (int i = 0; i < length; i++) {
            x ^= nums[i];
            x ^= i;
        }
        x ^= length;
        return x;
    }
}

C++

class Solution {
public:
    int missingNumber(vector<int> &nums) {
        int length = nums.size();
        int ret = 0;
        for (int i = 0; i < length; ++i) {
            ret ^= nums[i];
            ret ^= i;
        }
        ret ^= length;
        return ret;
    }
};

时间复杂度:O(n)O(n)

空间复杂度:O(1)O(1)

最后🌅

该篇文章为 「LeetCode」 系列的 No.20 篇,在这个系列文章中:

  • 尽量给出多种解题思路
  • 提供题解的多语言代码实现
  • 记录该题涉及的知识点

👨‍💻争取做到 LeetCode 每日 1 题,所有的题解/代码均收录于 「LeetCode」 仓库,欢迎随时加入我们的刷题小分队!