1.题目介绍
题目地址(645. 错误的集合 - 力扣(LeetCode))
https://leetcode.cn/problems/set-mismatch/
题目描述
集合 s
包含从 1
到 n
的整数。不幸的是,因为数据错误,导致集合里面某一个数字复制了成了集合里面的另外一个数字的值,导致集合 丢失了一个数字 并且 有一个数字重复 。
给定一个数组 nums
代表了集合 S
发生错误后的结果。
请你找出重复出现的整数,再找到丢失的整数,将它们以数组的形式返回。
示例 1:
输入:nums = [1,2,2,4] 输出:[2,3]
示例 2:
输入:nums = [1,1] 输出:[1,2]
提示:
2 <= nums.length <= 104
1 <= nums[i] <= 104
2. 题解
2.1 set集合求重复值 + 数学方法求丢失数
思路
只要找到了重复的数字rep_num, 1->n总和为sum(等差数列公式计算), 数组总和为Array_sum(在第一个循环中即可计算), 丢失的数字abandon_num , 有 abandon_num - rep_num= sum - totalSum ; 就可以计算出来了
代码
- 语言支持:C++
C++ Code:
class Solution {
public:
vector<int> findErrorNums(vector<int>& nums) {
long long totalSum = 0;
int rep_num, n = nums.size();
set<int> s;
for (int i = 0; i < n; i++){
if(!s.count(nums[i])){
s.emplace(nums[i]);
}else{
rep_num = nums[i];
}
totalSum += nums[i];
}
long long sum = (1 + n) * n / 2;
int abandon_num = sum - totalSum + rep_num;
vector<int> ans;
ans.push_back(rep_num);
ans.push_back(abandon_num);
return ans;
}
};
复杂度分析
令 n 为数组长度。
- 时间复杂度:\(O(n)\)
- 空间复杂度:\(O(n)\)
2.2 unordered_map哈希表
思路
unordered_map记录出现次数, 若为0则是丢失的数字, 若为2则是重复的数字
代码
class Solution {
public:
vector<int> findErrorNums(vector<int>& nums) {
vector<int> errorNums(2);
int n = nums.size();
unordered_map<int, int> mp;
for (auto& num : nums) {
mp[num]++;
}
for (int i = 1; i <= n; i++) {
int count = mp[i];
if (count == 2) {
errorNums[0] = i;
} else if (count == 0) {
errorNums[1] = i;
}
}
return errorNums;
}
};
复杂度分析
令 n 为数组长度。
- 时间复杂度:\(O(n)\)
- 空间复杂度:\(O(n)\)
2.3 位运算
思路
注意点:
1.x xor x = 0, x xor 0 = x;
2.我们后续再引入1-n 这n个数为的是将无关数排除出去, 只留下出现了三次的 rep_num 和 出现了一次的 abandon_num
3.求最低位的思路来自我们想要将这两个数分开, 必须有一个能区分这两个数的标志, 便利用 二进制负数的性质获得了二者不同的最低位.
4.如果xor = [0 1 0 0 1 0 0 0]
则 (- xor) = [1 0 1 1 1 0 0 0]
那么 lowbit = xor & (-xor) = [0 0 0 0 1 0 0 0],lowbit 就是 x 和 y 的二进制表示中的最低不同位。
代码
class Solution {
public:
vector<int> findErrorNums(vector<int>& nums) {
int n = nums.size();
int xorSum = 0;
for(int i = 0; i < n; i++){
xorSum ^= nums[i]; // 先加入数组的所有数
}
// 加入1-n这n个数(为了使无关数出现两次,异或为0)
for(int i = 1; i <= n; i++){
xorSum ^= i; // 再加入1-n这n个数, 由于除了丢失的数和重复的数, 在这次计算后都是异或了两次, x ^ x = 0, 所以 相当于 xor = rep_num ^ rep_num ^ rep_num ^ abandon_num = rep_num ^ abandon_num
}
int low_bit = xorSum & (-xorSum); // 由于 -xor 等于所有位取反,再加一, 这里就可以获得两个数不同的最低位,方便后面分组将其区分开来
// 求得最低位, 对2n个数进行处理
int num1 = 0, num2 = 0;
// 先处理2n个数中 数组的数
for(int num : nums){
if(num & low_bit){
num1 ^= num;
} else{
num2 ^= num;
}
}
// 处理2n个数中的 1-n
for(int i = 1; i <= n; i++){
if(i & low_bit){
num1 ^= i;
} else{
num2 ^= i;
}
}
// 判断重复数,返回数组
for(int num: nums){
if(num == num1){
return vector<int>{num1, num2};
}
}
return vector<int>{num2, num1};
}
};
复杂度分析
令 n 为数组长度。
- 时间复杂度:\(O(n)\)
- 空间复杂度:\(O(1)\)