0x01 题目要求
给定一个包含 [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 中的所有数字都 独一无二
0x02 解题思路
哈希集合
- 有题意可知,我们 需要在长度为 n 的数组中,找到 0 - n 之间丢失的那个数字
- 最开始我想到的就是使用 哈希集合 来求解
- 创建一个哈希集合,遍历一次元素数组,将每一个元素都存放在哈希集合当中
- 当所有元素存放完后,需要 再遍历一次 0 - n ,在哈希集合中找到不存在的那个元素值
代码如下:
// 哈希集合
class Solution
{
public:
int missingNumber(vector<int>& nums)
{
int n = nums.size();
// 创建一个哈希集合,用于存放 nums 数组中所有值后查找丢失值
unordered_set<int> hashset;
// 循环遍历 nums 中的所有元素值为 e
// 并将每一个元素值 e 插入到哈希集合中
for (int e : nums)
hashset.insert(e);
// 遍历 0 - n ,在哈希集合中查找到丢失的元素
for (int i=0;i<=n;i++)
if (!hashset.count(i))
// 当没有找到 0 - n 中的某一个值时
// 代表这个值为丢失值,直接将 i 返回即可
return i;
return n;
}
};
排序
- 很明显使用哈希集合求解需要花费大量的时间
- 既然每一个元素都在 0 - n 之间,且只出现一次
- 那么我们可以 将数组元素先排序
- 然后 再遍历一遍排序完的数组
- 当当前元素值与位置下标不符合时
- 代表当前下标位置的值即是丢失的值
代码如下:
// 排序
class Solution
{
public:
int missingNumber(vector<int>& nums)
{
int n = nums.size();
// 使用 C++ 标准库中的排序函数将 nums 元素进行排序
// 默认排序方法为 从小到大
sort(nums.begin(), nums.end());
// 循环遍历排序完 nums 数组中所有值
for (int i=0;i<n;i++)
// 当当前 nums[i] != i ,即当前值与位置值不符时
// 代表已经找到了丢失值,此时将 i 返回即可
if (nums[i] != i)
return i;
return n;
}
};
异或
- 在之前文章 Leetcode 136.只出现一次的数字Ⅰ 中用到过异或进行求解
- 具体的异或知识可以点链接跳转查看
- 以此作为启发,我们 先将一个初始化为 0 的变量 result 与 nums 中的每一个元素进行异或
- 然后再将 result 与 0 - n 的每一个元素进行一次异或
- 那么,在 0 - n 中,只有丢失的那个值只进行了一次异或
- 其他值都进行了两次异或,相当于 A ^ A ^ B ^ B … ^ Y ^ Y ^ Z = 0 ^ Z
- 最后就是 0 与 丢失的那个值进行一次异或
- 0 与任何值异或结果都为任何值本身
- 最后返回 result 即可
代码如下:
// 异或
class Solution
{
public:
int missingNumber(vector<int>& nums)
{
int n = nums.size();
// 创建一个结果变量 result = 0
int result = 0;
// 先将 nums 中的每一个元素值都与 result 相异或
for (int num : nums)
result ^= num;
// 再将 result 与 0 - n 每一个元素相异或
// 由于只有丢失的那个值与 result 异或一次
// 其他值都异或了两次,异或两次的值为 0
// 最后就相当于 0 与 丢失值相互异或
// 最后 result 就等于丢失值
for (int i=0;i<=n;i++)
result ^= i;
// 返回 result 即可
return result;
}
};
数学求解
- 在 0 - n 之间找到数组中丢失的值
- 在这里我们使用 高斯求和 公式直接算得 sum 值
- 那么当我们把 0 - n 之间所有元素相加求和获得 sum 之后
- 再用 sum 减去当前 nums 数组中的每一个 num 元素
- 最后留下的数值,就是我们需要找的丢失的值
代码如下:
// 数学求解
class Solution
{
public:
int missingNumber(vector<int>& nums)
{
int n = nums.size();
// 将 0 - n 的所有元素相加求和的值记为 sum
// 通过高斯求和公式即可算出 sum 的大小
int sum = n * (n + 1) / 2;
// 遍历循环 nums 数组中的每一个元素记为 num
// 将每一个元素 num 都从 sum 中减去
for (int num : nums)
sum -= num;
// 减到最后剩下的值即为丢失的值
return sum;
}
};
0x03 测试结果
哈希集合
- 时间复杂度:O(n),遍历数组元素的时间复杂度为O(n),判断 0 - n 的每个整数是否在哈希集合中的时间复杂度也是O(n)。
- 空间复杂度:O(n),n 为 nums 的长度,哈希集合需要存放 n 个数组元素。
排序
- 时间复杂度:O(nlogn),排序的时间复杂度为O(nlogn),遍历数组找到丢失值的时间复杂度为O(n),因此总的时间复杂度为O(nlogn)。
- 空间复杂度:O(logn),空间复杂度只要是取决于在排序时递归调用栈的空间。
异或
- 时间复杂度:O(n),异或数组的时间复杂度为O(n),异或 0 - n 之间元素的时间复杂度也是O(n)。
- 空间复杂度:O(1),并不需要开辟额外的空间。
数学求解
- 时间复杂度:O(n),高斯求和 sum 需要O(1)的时间,遍历 nums 数组每次减去一个元素的时间复杂度为O(n)。
- 空间复杂度:O(1),并不需要开辟额外的空间。