集合 S 包含从1到 n 的整数。不幸的是,因为数据错误,导致集合里面某一个元素复制了成了集合里面的另外一个元素的值,导致集合丢失了一个整数并且有一个元素重复。
给定一个数组 nums 代表了集合 S 发生错误后的结果。你的任务是首先寻找到重复出现的整数,再找到丢失的整数,将它们以数组的形式返回。
示例 1:
输入: nums = [1,2,2,4]
输出: [2,3]
注意:
给定数组的长度范围是 [2, 10000]。
给定的数组是无序的。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/set-mismatch
方法一:暴力解法
最直接的做法就是单独检查 1 到 n 的所有数字。在检查每个数字时都遍历整个 nums 数组,检查当前数字在 nums 中是否出现了两次,或者一次都没有出现。使用 dup 和 missing 记录重复数字和缺失数字,因此一旦找到这两个数字,就可以提前结束查找过程。
public static int[] findErrorNums(int[] nums) {
int dup = -1, missing = -1;
for (int i = 1; i <= nums.length; i++) {
int count = 0;
for (int j = 0; j < nums.length; j++) {
if (nums[j] == i)
count++;
}
if (count == 2)
dup = i;
else if (count == 0)
missing = i;
if (dup > 0 && missing > 0)
break;
}
return new int[]{
dup, missing};
}
方法二:使用排序
排序 nums 数组后,相等的两个数字将会连续出现。此外,检查相邻的两个数字是否只相差 1 可以找到缺失数字。
public static int[] findErrorNums2(int[] nums) {
Arrays.sort(nums);
int dup = -1, missing = 1;
for (int i = 1; i < nums.length; i++) {
if (nums[i] == nums[i - 1])
dup = nums[i];
else if (nums[i] > nums[i - 1] + 1)
missing = nums[i - 1] + 1;
}
return new int[]{
dup, nums[nums.length - 1] != nums.length ? nums.length : missing};
}
方法三:使用 Map
-
使用 map 存储 nums 中每个数字的出现次数。存储形式为 (num_i, count_i) 表示数字 num_i 一共出现了count_i 次。当一个元素重复出现时,它的计数就加 1。
-
再检查 1 到 n 的每个数字在 map 中出现次数。如果一个数字在 map 中没有出现,它就是缺失数字。如果一个数字的出现了两次,它就是重复数字。
public static int[] findErrorNums3(int[] nums) {
Map<Integer, Integer> map = new HashMap();
int dup = -1, missing = 1;
for (int n : nums) {
if (map.get(n) == null)
map.put(n, 1);
else map.put(n, map.get(n) + 1);
}
for (int i = 1; i <= nums.length; i++) {
if (map.get(i) == null)
missing = i;
else if (map.get(i).intValue() == 2)
dup = i;
}
return new int[]{
dup, missing};
}
方法四:使用额外数组
如果使用 map 存储每个数字出现的次数,每个数字都需要两个变量分别存储数字本身和出现次数,因此存储 n 个数字和出现次数需要 2n 的空间。如果使用数组 arrarr 代替 map,可以将空间减少到 n。
在数组 arr 中,索引代表数字,arr 存储每个数字出现的次数。例如 arr[i] 存储数字 i 出现的次数。
public int[] findErrorNums4(int[] nums) {
int[] arr = new int[nums.length + 1];
int dup = -1, missing = 1;
for (int i = 0; i < nums.length; i++) {
arr[nums[i]] += 1;
}
for (int i = 1; i < arr.length; i++) {
if (arr[i] == 0)
missing = i;
else if (arr[i] == 2)
dup = i;
}
return new int[]{
dup, missing};
}
方法五:使用额外空间
已知 nums 中所有数字都是正数,且处于 1 到 n 之间。遍历 nums 中的所有数字,根据数字 i 找到 nums[∣i∣],如果是第一次访问 nums[∣i∣],将它反转为负数。如果是第二次访问,则会发现它已经是负数。因此,可以根据访问一个数字时它是否为负数找出重复数字。
完成上述操作后,所有出现过的数字对应索引处的数字都是负数,只有缺失数字 j 对应的索引处仍然是正数。
public static int[] findErrorNums5(int[] nums) {
int dup = -1, missing = 1;
for (int n: nums) {
if (nums[Math.abs(n) - 1] < 0)
dup = Math.abs(n);
else
nums[Math.abs(n) - 1] *= -1;
}
for (int i = 1; i < nums.length; i++) {
if (nums[i] > 0)
missing = i + 1;
}
return new int[]{
dup, missing};
}