LC 645. 错误的集合
题目描述
这是 LeetCode 上的 645. 错误的集合 ,难度为 简单。
集合 s
包含从 1
到 n
的整数。不幸的是,因为数据错误,导致集合里面某一个数字复制了成了集合里面的另外一个数字的值,导致集合 丢失了一个数字 并且 有一个数字重复 。
给定一个数组 nums
代表了集合 S
发生错误后的结果。
请你找出重复出现的整数,再找到丢失的整数,将它们以数组的形式返回。
示例 1:1
2输入:nums = [1,2,2,4]
输出:[2,3]
示例 2:1
2输入:nums = [1,1]
输出:[1,2]
提示:
- 2 <= nums.length <= $10^4$
- 1 <= nums[i] <= $10^4$
计数
一个朴素的做法是,使用「哈希表」统计每个元素出现次数,然后在 $[1, n]$ 查询每个元素的出现次数。
在「哈希表」中出现 $2$ 次的为重复元素,未在「哈希表」中出现的元素为缺失元素。
由于这里数的范围确定为 $[1, n]$,我们可以使用数组来充当「哈希表」,以减少「哈希表」的哈希函数执行和冲突扩容的时间开销。
代码:1
2
3
4
5
6
7
8
9
10
11
12
13class Solution {
public int[] findErrorNums(int[] nums) {
int n = nums.length;
int[] cnts = new int[n + 1];
for (int x : nums) cnts[x]++;
int[] ans = new int[2];
for (int i = 1; i <= n; i++) {
if (cnts[i] == 0) ans[1] = i;
if (cnts[i] == 2) ans[0] = i;
}
return ans;
}
}
- 时间复杂度:$O(n)$
- 空间复杂度:$O(n)$
数学
我们还可以利用数值范围为 $[1, n]$,只有一个数重复和只有一个缺失的特性,进行「作差」求解。
- 令 $[1, n]$ 的求和为 $tot$,这部分可以使用「等差数列求和公式」直接得出:$tot = \frac{n (1 + n)}{2}$ ;
- 令数组 $nums$ 的求和值为 $sum$,由循环累加可得;
- 令数组 $sums$ 去重求和值为 $set$,由循环配合「哈希表/数组」累加可得。
最终答案为 (重复元素, 缺失元素) = (sum-set, tot-set)
。
代码;1
2
3
4
5
6
7
8
9
10
11
12
13
14class Solution {
public int[] findErrorNums(int[] nums) {
int n = nums.length;
int[] cnts = new int[n + 1];
int tot = (1 + n) * n / 2;
int sum = 0, set = 0;
for (int x : nums) {
sum += x;
if (cnts[x] == 0) set += x;
cnts[x] = 1;
}
return new int[]{sum - set, tot - set};
}
}
- 时间复杂度:$O(n)$
- 空间复杂度:$O(n)$
桶排序
因为值的范围在 $[1, n]$,我们可以运用「桶排序」的思路,根据 $nums[i] = i + 1$ 的对应关系使用 $O(n)$ 的复杂度将每个数放在其应该落在的位置里。
然后线性扫描一遍排好序的数组,找到不符合 $nums[i] = i + 1$ 对应关系的位置,从而确定重复元素和缺失元素是哪个值。
代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23class Solution {
public int[] findErrorNums(int[] nums) {
int n = nums.length;
for (int i = 0; i < n; i++) {
while (nums[i] != i + 1 && nums[nums[i] - 1] != nums[i]) {
swap(nums, i, nums[i] - 1);
}
}
int a = -1, b = -1;
for (int i = 0; i < n; i++) {
if (nums[i] != i + 1) {
a = nums[i];
b = i == 0 ? 1 : nums[i - 1] + 1;
}
}
return new int[]{a, b};
}
void swap(int[] nums, int i, int j) {
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
}
- 时间复杂度:$O(n)$
- 空间复杂度:$O(1)$
最后
这是我们「刷穿 LeetCode」系列文章的第 No.645
篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。
在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。
为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode 。
在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!