题目来源:https://leetcode.cn/problems/get-kth-magic-number-lcci/submissions/
大致题意:
给一个数组,数组中有 n - 2 个元素,分别是 1 - n 中的 n - 2 个不同的数字,求出 1 - n 中不在数组中的那两个数字。
要求时间复杂度为 O(n),空间复杂度为 O(1)
思路
对于这类要求在 O(1) 复杂度找出数组中符合某种规律的数,一般都使用位运算来解题
对于该题,我们已知所有数都在 1 - n 中,于是可以直接求出 1 - n 的异或值,然后再对整个数组求异或值,那么该题其实类似力扣 剑指 Offer 56 - I. 数组中数字出现的次数
位运算
已知两个相同值异或的结果为 0,且异或满足交换律和结合律,那么将整个数组加上 1 - n 进行异或运算,只有 1 - n 中不在数组中出现的两个数在异或过程中出现了一次,其余数都出现了两次,也就是说,最终异或的结果相等于 1 - n 在数组没出现那两个数的异或值
已知这两个数的异或结果,如果求出这两个数?
可以用力扣 剑指 Offer 56 - I. 数组中数字出现的次数中的解法
- 求出异或值中最低位的 1,求出这个二进制值(比如异或结果为 10010100,这个二进制即为 100)。该位即为所求两个数对应二进制中不相同的最低位,此时 1 - n 的所有数可以分为两部分,在该位为 1 的、在该位为 0的
- 遍历数组,分别求出两组数的异或值
- 遍历 1 - n,分别求出两组数的异或值
- 将上述两组数异或值再进行异或,那么剩下的两个数即为所求数
具体看代码:
class Solution {
public int[] missingTwo(int[] nums) {
int xor = 0;
int n = nums.length;
// 计算 1 - n 的异或值
for (int i = 1; i <= n + 2; i++) {
xor ^= i;
}
int aXorB = xor;
// 将上述异或值与整个数组进行运算,得到所求两个数的异或值
for (int i = 0; i < n; i++) {
aXorB ^= nums[i];
}
// 使用该方法可以快速求出异或结果二进制中最低位的 1
int diff = aXorB & (-aXorB);
int a = 0;
int b = 0;
// 分组对数组进行异或
for (int i = 0; i < n; i++) {
if ((nums[i] & diff) == 0) {
a ^= nums[i];
} else {
b ^= nums[i];
}
}
// 分组对 1 - n 进行异或
for (int i = 1; i <= n + 2; i++) {
if ((i & diff) == 0) {
a ^= i;
} else {
b ^= i;
}
}
// 返回结果
return new int[]{a, b};
}
}