【面试题 17.19. 消失的两个数字】

来源:力扣(LeetCode)

描述:

  给定一个数组,包含从 1 到 N 所有的整数,但其中缺了两个数字。你能在 O(N) 时间内只用 O(1) 的空间找到它们吗?

以任意顺序返回这两个数字均可。

示例 1:

输入: [1]
输出: [2,3]

示例 2:

输入: [2,3]
输出: [1,4]

提示:

  • nums.length <= 30000

方法:位运算

思路与算法

寻找消失的数字,最直观的方法是使用哈希表存储数组中出现过的数字。由于这道题有时间复杂度 O(n) 和空间复杂度 O(1) 的要求,因此不能使用哈希表求解,必须使用其他方法。利用位运算的性质,可以达到时间复杂度 O(n) 和空间复杂度 O(1)。

由于从 1 到 n 的整数中有两个整数消失,其余每个整数都在数组中出现一次,因此数组的长度是 n - 2 。在数组中的 n - 2 个数后面添加从 1 到 n 的每个整数各一次,则得到 2n - 2 个数字,其中两个在数组中消失的数字各出现一次,其余每个数字各出现两次。

假设数组 nums 中消失的两个数字分别是 x1 和 x2。如果把上述 2n - 2 个数字全部异或起来,得到结果 x,那么一定有:

x = x 1 ⊕ x 2

其中 ⊕ 表示异或运算。这是因为 nums 中出现两次的元素都会因为异或运算的性质 a ⊕ b ⊕ b = a 抵消掉,那么最终的结果就只剩下 x1 和 x2 的异或和。

显然 x != 0,因为如果 x = 0 ,那么说明x1 = x2,这样 x1和 x2 就不是在上述 2n - 2 个数字中只出现一次的数字了。因此,我们可以使用位运算 x & -x 取出 x 的二进制表示中最低位那个 1,设其为第 l 位,那么 x1 和 x2 中的某一个数的二进制表示的第 l 位为 0 ,另一个数的二进制表示的第 l 位为 1。在这种情况下,x1 ⊕ x2 的二进制表示的第 l 位才能为 1。

这样一来,我们就可以把从 1 到 n 的所有整数分成两类,其中一类包含所有二进制表示的第 l 位为 0 的数,另一类包含所有二进制表示的第 l 位为 1 的数。可以发现:

  • 对于任意一个在数组 nums 中出现一次的数字,这些数字在上述 2n - 2 个数字中出现两次,两次出现会被包含在同一类中;

  • 对于任意一个在数组 nums 中消失的数字,即 x1 和 x2,这些数字在上述 2n - 22n−2 个数字中出现一次,会被包含在不同类中。

因此,如果我们将每一类的元素全部异或起来,那么其中一类会得到 x1 ,另一类会得到 x2 。这样我们就找出了这两个只出现一次的元素。

代码:

class Solution {
public:
    vector<int> missingTwo(vector<int>& nums) {
        int xorsum = 0;
        int n = nums.size() + 2;
        for (int num : nums) {
            xorsum ^= num;
        }
        for (int i = 1; i <= n; i++) {
            xorsum ^= i;
        }
        // 防止溢出
        int lsb = (xorsum == INT_MIN ? xorsum : xorsum & (-xorsum));
        int type1 = 0, type2 = 0;
        for (int num : nums) {
            if (num & lsb) {
                type1 ^= num;
            } else {
                type2 ^= num;
            }
        }
        for (int i = 1; i <= n; i++) {
            if (i & lsb) {
                type1 ^= i;
            } else {
                type2 ^= i;
            }
        }
        return {type1, type2};
    }
};

执行用时:20 ms, 在所有 C++ 提交中击败了94.04%的用户
内存消耗:21.8 MB, 在所有 C++ 提交中击败了61.28%的用户
复杂度分析
时间复杂度:O(n),其中 n 是最大的整数。需要遍历的数字有 2n − 2 个,共遍历两次。
空间复杂度:O(1)。
author:LeetCode-Solution

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千北@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值