Leetcode-442-数组中重复的数据

Leetcode-442-数组中重复的数据

思路:自哈希/抽屉原理

题目描述

给定一个整数数组 a,其中1 ≤ a[i] ≤ n (n为数组长度), 其中有些元素出现两次而其他元素出现一次。

找到所有出现两次的元素。

你可以不用到任何额外空间并在O(n)时间复杂度内解决这个问题吗?

1
2
3
4
5
输入:
[4,3,2,7,8,2,3,1]

输出:
[2,3]

方法:抽屉原理

  • 思路分析:“桶排序”的思想是“抽屉原理”,即“一个萝卜一个坑”,8 个萝卜要放在 7 个坑里,则至少有 1 个坑里至少有 2 个萝卜。

  • 这里由于数组元素限定在数组长度的范围内,因此,我们可以通过一次遍历:

    让数值 1 就放在索引位置 0 处;
    让数值 2 就放在索引位置 1 处;
    让数值 3 就放在索引位置 2 处;

  • 一次遍历以后,那些“无处安放”的元素就是我们要查找的“出现两次的元素”

  • 为了不使用额外的空间,这里使用到的一个技巧是“基于异或运算交换两个变量的值”:交换两个整数,除了引入一个新的变量,写出一个“轮换”的赋值表达式以外,还有两种比较 tricky 的做法,下面给出结论。

    • 如果 a ^ b = c 那么 a ^ c = bb ^ c = a同时成立
基于异或运算 基于加减法
a = a ^ b b = a ^ b a = a ^ b a = a + b b = a - b a = a - b
  • 对于异或运算实现的交换方法,如果调用 swap(nums, i, i),那么最终的结果会变为 0
  • 对于加减法实现的交换方法,有可能发生溢出。
1
2
3
4
5
6
7
8
9
10
11
12
13
// 基与异或的交换方法
private void swap(int[] nums,int index1,int index2){
if (index1 == index2){
return;
}

// 左边a b a
// 右边相同 :两个数字的异或
nums[index1] = nums[index1] ^ nums[index2];
nums[index2] = nums[index1] ^ nums[index2];
nums[index1] = nums[index1] ^ nums[index2];

}

解法:

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
class Solution {
public List<Integer> findDuplicates(int[] nums) {
// 记录结果
List<Integer> res = new ArrayList<>();

int len = nums.length;

// 特判
if (len == 0){
return res;
}

// 对数组自己做哈希:数值为i的数字映射到下标 i - 1的位置
for (int i = 0; i < len; i++) {
// 在指定范围内,如果数字并且没有放在正确的位置上,才交换
// 例如:数值3应该放在索引2的位置上
while (nums[nums[i] - 1] != nums[i]){
swap(nums,i,nums[i] - 1);
}
}

// 遍历交换后的数组,如果当前下标和数字不对应
// 说明出现了重复的数字,加入到res中
for (int i = 0; i < len; i++) {
if (nums[i] - 1 != i){
res.add(nums[i]);
}
}

return res;
}


// 基与异或的交换方法
private void swap(int[] nums,int index1,int index2){
if (index1 == index2){
return;
}

// 左边a b a
// 右边相同 :两个数字的异或
nums[index1] = nums[index1] ^ nums[index2];
nums[index2] = nums[index1] ^ nums[index2];
nums[index1] = nums[index1] ^ nums[index2];
}


// // 普通交换函数
// private void swap(int[] nums, int i, int j) {
// int temp = nums[i];
// nums[i] = nums[j];
// nums[j] = temp;
// }
}

复杂度分析:

  • 时间复杂度:O(N),这里 N 是数组的长度。(均摊复杂度)
  • 空间复杂度:O(1),这里因为使用异或运算交换数组的元素,故没有使用额外的数组空间。
打赏
  • 版权声明: 本博客所有文章除特别声明外,均采用 Apache License 2.0 许可协议。转载请注明出处!
  • © 2019-2022 Zhuuu
  • PV: UV:

请我喝杯咖啡吧~

支付宝
微信