《LeetCode之每日一题》:146.错误的集合

Posted 是七喜呀!

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《LeetCode之每日一题》:146.错误的集合相关的知识,希望对你有一定的参考价值。

错误的集合


题目链接: 错误的集合

有关题目

集合 s 包含从 1 到 n 的整数。
不幸的是,因为数据错误,导致集合里面某一个数字复制了成了集合里面的另外一个数字的值,
导致集合 丢失了一个数字 并且 有一个数字重复 。

给定一个数组 nums 代表了集合 S 发生错误后的结果。

请你找出重复出现的整数,再找到丢失的整数,将它们以数组的形式返回。
示例 1:

输入:nums = [1,2,2,4]
输出:[2,3]
示例 2:

输入:nums = [1,1]
输出:[1,2]
提示:

2 <= nums.length <= 10^4
1 <= nums[i] <= 10^4

题解

法一:排序
参考官方题解

思路:
先排序,使用变量pre记录前一个数字,cur记录当前数字
通过观察我们可以发现,
①缺失元素在下标为1 ~ n - 1时,必定存在cur - pre = 2;
②下标为1 或 n - 1,我们特殊判断

返回值第一个值我们存放重复数字,第二个值我们存放缺失数字

class Solution {
public:
    vector<int> findErrorNums(vector<int>& nums) {
        int n = nums.size();
        vector<int> ans(2);
        sort(nums.begin(), nums.end());
        int pre = 0;
        for (int i = 0; i < n; ++i){
            int cur = nums[i];
            if (pre == cur){
                ans[0] = cur;//存放的重复的数字
            } else if (cur - pre > 1){
                ans[1] = pre + 1;
            }
            pre = cur;
        }
        if (nums[n - 1] != n){
            ans[1] = n;
        }
        return ans;
    }
};

法二:哈希计数

class Solution {
public:
    vector<int> findErrorNums(vector<int>& nums) {
        int n = nums.size();
        unordered_map<int, int> mp;
        vector<int> ans(2, 0);
        for (int i = 0; i < n; ++i){
        	//for (auto& num : nums)
            ++mp[nums[i]];
        }
        for (int i = 1; i <= n; ++i){
            if (mp[i] == 0){
                ans[1] = i;
            }
            if (mp[i] == 2){
                ans[0] = i;
            }
        }
        return ans;
    }
};


法三:位运算
参考官方题解

思路:
①类比题目找到1 ~ n中缺失的一个数字,我们自然而然会想到
数组中增加1 ~ n中全部的数字,并进行异或运算,得到xorSum。

②找出数组中两个出现奇数次的数字,其他出现偶数次数字,
通过lowBit = xorSum & (- xorSum) 找出最低不同二进制位,得出答案
class Solution {
public:
    vector<int> findErrorNums(vector<int>& nums) {
        int n = nums.size();
        int xorSum = 0;
        for (auto& num : nums){
            xorSum ^= num;
        }
        for (int i = 1; i <= n; ++i){
            xorSum ^= i;
        }
        int lowBit = xorSum - (xorSum & (xorSum - 1));//xorSum & (-xorSum)
        int num1 = 0, num2 = 0;
        for (auto& num : nums){
            if ((lowBit & num) == 0){//注意lowBit是拿到最低二进制位的 1,不一定表示 1
                num1 ^= num;
            } else {
                num2 ^= num;
            }
        }
        for (int i = 1; i <= n; ++i){
            if ((lowBit & i) == 0){
                num1 ^= i;
            } else {
                num2 ^= i;
            }
        }
        for (auto& num : nums){
            if (num == num1){
                return vector<int>{num1, num2};
            }
        }
        return vector<int> {num2, num1};
    }
};


法四:原地置换
参考官方题解评论区Jeff Wong

思路:
①数组中元素为1 ~ n,且有重复元素,故我们可以采用映射下标并标记为负数的方式找出重复元素ans[0]
②缺失数字,在映射下标的过程结束,所对应的数组值一定为正数,(当然过程中会存在负数的状态)
从找规律的角度理解,我们给出下面两组例子:
示例一;
//2 3 3 4映射下标并标记为负数过程结束,数组中元素变为
//2 -3 -3 -4,找规律发现,在该下标满足i + 1 != ans[0],且对应元素 > 0情况,则下标为 i 的元素 加 1即为缺失元素
示例二:
//1 2 2 4映射下标并标记为负数的过程结束,数组中元素变为
//-1 2 2 -4 找规律发现,在该下标满足 i + 1 != ans[0],
且 nums[i] > 情况下,则下标为 i 的元素 加 1 则为缺失数字
class Solution {
public:
    vector<int> findErrorNums(vector<int>& nums) {
        int n = nums.size();
        vector<int> ans(2);
        for (int i = 0; i < n; ++i){
            int idx = abs(nums[i]) - 1;
            if (nums[idx] < 0){//找出重复的元素
                ans[0] = idx + 1;
            }
            nums[idx] = -nums[idx];
        }

        for (int i = 0; i < n; ++i){
            if (nums[i] > 0 && i + 1 != ans[0]){//缺失的元素,下标映射一定为
                ans[1] = i + 1;
            }
        }
        return ans;
    }
};

时间复杂度:O(n),找出重复数字O(n),找出缺失数字O(n),故总的时间复杂度O(n)
空间复杂度:O(1)

以上是关于《LeetCode之每日一题》:146.错误的集合的主要内容,如果未能解决你的问题,请参考以下文章

《LeetCode之每日一题》:59.第一个错误的版本

《LeetCode之每日一题》:205.合并区间

《LeetCode之每日一题》:78.无重复字符的最长字串

《LeetCode之每日一题》:280.矩阵置零

每日一题之LeetCode 栈简单题集合496,682,232,225,155,844,20

《LeetCode之每日一题》:127.删除注释