1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public int[] findErrorNums(int[] nums) {
int repeat = -1;
for (int i = 0; i < nums.length; i++) {
int targetPos = Math.abs(nums[i]) - 1;
if (nums[targetPos] < 0) {
repeat = Math.abs(nums[i]);
} else {
nums[targetPos] *= -1;
}
}
int missing = -1;
for (int i = 0; i < nums.length; i++) {
if (nums[i] > 0) {
missing = i + 1;
}
}
return new int[] { repeat, missing };
}
}

在原数组上标记.

时间复杂度: O(n)
空间复杂度: O(1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class Solution {
public int[] findErrorNums(int[] nums) {
int xor = 0, xor0 = 0, xor1 = 0;
for (int n : nums)
xor ^= n;
for (int i = 1; i <= nums.length; i++)
xor ^= i;
int rightmostbit = xor & ~(xor - 1);
for (int n : nums) {
if ((n & rightmostbit) != 0)
xor1 ^= n;
else
xor0 ^= n;
}
for (int i = 1; i <= nums.length; i++) {
if ((i & rightmostbit) != 0)
xor1 ^= i;
else
xor0 ^= i;
}
for (int i = 0; i < nums.length; i++) {
if (nums[i] == xor0)
return new int[] { xor0, xor1 };
}
return new int[] { xor1, xor0 };
}
}

首先把nums中的元素互相XOR然后把得到的结果再和[1, n]XOR. 最后得到的结果是missing ^ repeat = mrx. 如果把missing和repeat区分出来? 我们知道XOR是相同bit得0, 不同bit得1. mrx的最后边一个bit所在的位置 表示该位置一个num是1, 一个num是0. 于是我们可以用mrx & (mrx - 1)来得到只有在这个位置是1的这个数. 这是因为(mrx - 1)对该位置左侧不影响, 只是flip, 于是它和mrx进行and后的该位置左侧都是0. 对于该位置右侧 由于减去1, 该位置右侧都变为1(如果右侧有bits的话), 该位置变为0. 然后flip后, 该位置变为1, 该位置右侧变为0, 和mrx and后, 由于mrx在该位置本来就是1, 于是最后的结果就是只在该位置是1, 其他位置都是0.

然后我们再去XOR nums中的所有num, 其中在该位置是1的数字和xor1进行xor, 否则和xor2进行xor. 之后再对[1, n] 进行同样的操作. 我们假设重复的数字在该位置是1, 那么此时xor1存的就是repeat; 类似地如果重复的数字在该位置不是1, 此时xor0就是repeat. 最后一个for循环看是否xor0等于nums中的某个num, 如是, 那么xor0存的就是重复的数字, 否则xor1存的就是重复的数字.

很巧妙的方法. 需要记得这个方法.

时间复杂度: O(n)
空间复杂度: O(1)