题目描述
给定一个包含 n-2 个不同整数的数组,这些整数取自范围 [1, n]。请找出缺失的那两个数字。
示例: 输入:nums = [1] 输出:[2, 3] 解释:n=3,数组中只有 1,缺失的是 2 和 3。
输入:nums = [2, 3] 输出:[1, 4] 解释:n=4,数组中有 2 和 3,缺失的是 1 和 4。
解题思路
这道题是经典位运算问题的变种。核心思想利用异或(XOR)运算的特性:
- 相同为 0,不同为 1:任何数与自身异或结果为 0,与 0 异或结果为其本身。
- 交换律与结合律:异或运算的顺序不影响最终结果。
如果我们把数组中的所有元素与 1 到 n+2 的所有整数进行异或,那么出现两次的数字会互相抵消变为 0,最终剩下的结果就是那两个缺失数字的异或值(记为 ret)。
假设缺失的两个数为 A 和 B,则 ret = A ^ B。因为 A != B,所以 ret 一定不为 0,这意味着在二进制表示中至少有一位是 1。我们可以找到这一位,将原数组和 1 到 n+2 的数字根据该位是 0 还是 1 分成两组。A 和 B 必然分属不同的组,而相同的数字依然会在同一组内成对出现。分别对两组进行异或,即可分别得到 A 和 B。
代码实现
这里提供两种基于异或分组的具体写法。第一种通过循环查找差异位,第二种利用位运算技巧直接获取最低有效位。
方案一:通用位拆分
这种方法直观地寻找 ret 中任意一个为 1 的比特位,然后进行分组。
class Solution {
public:
vector<int> missingTwo(vector<int>& nums) {
int ret = 0;
// 第一步:计算所有数字的异或和
for (auto x : nums) ret ^= x;
for (int i = 1; i <= nums.size() + 2; i++) ret ^= i;
// 第二步:找出 ret 中任意一个为 1 的位
int differ = 0;
while (!((ret >> differ) & 1)) {
differ++;
}
// 第三步:根据该位将数字分为两组分别异或
int a = 0, b = 0;
for (auto x : nums) {
((x >> differ) & ) b ^= x;
a ^= x;
}
( i = ; i <= nums.() + ; i++) {
((i >> differ) & ) b ^= i;
a ^= i;
}
{a, b};
}
};


