原始题目:剑指 Offer 03. 数组中重复的数字 (opens new window)

# 方法一:使用集合

遍历数组,对于每个数字,先判断集合中是否有该数字,如果没有则存入集合中,如果已经存在了,那么该数字就为重复的数字。

代码:

public int findRepeatNumber(int[] nums) {
    if (nums == null || nums.length == 1) {
        return -1;
    }
    Set<Integer> set = new HashSet<>();
    for (Integer n : nums) {
        if (set.contains(n)) {
            return n;
        } else {
            set.add(n);
        }
    }
    return -1;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

复杂度分析

  • 时间复杂度O(N)O(N):最坏的情况下,需要进行 NN 次循环,HashSet 的添加和查找都是 O(1)O(1) 的。

  • -空间复杂度O(N)O(N):HashSet 占用 O(N)O(N) 大小的空间。

# 方法二:原地交换

题目中说明了这个长度为 NN 的数组中的数字的范围在 [0,N1[0, N-1 中,因此假如每个数字都是唯一的,那么必定会有 nums[i]=inums[i] = i

现在题目中说明了有数字重复了,那么我们在遍历数组的时候,遍历到数字 aa 时,将 aa 交换到索引 aa 处,此时有 nums[a]=anums[a] = a ​。那么第二次遇到数字 aa 时,一定有 nums[a]=anums[a] = a 了,所以 aa 一定是重复的数字。

算法流程:

  1. 遍历数组 numsnums,初始索引位置 i=0i = 0
    1. nums[i]=inums[i] = i :说明 nums[i]nums[i] 已经在对应索引位置上了,无需交换。
    2. nums[nums[i]]=nums[i]nums[nums[i]] = nums[i] :说明 nums[i]nums[i]​ 处和索引 ii 处的元素值都为 nums[i]nums[i] ,那么 nums[i]nums[i] 就是一个重复的数字,返回值 nums[i]nums[i]
    3. 否则,交换索引 iinums[i]nums[i] 的元素值,将此数字交换至对应索引位置。
  2. 若遍历完尚未返回,则返回 1-1

代码:

    复杂度分析

    • 时间复杂度 O(N)O(N) 需要循环 NN 遍,每轮的判断和交换操作是 O(1)O(1)

    • 空间复杂度 O(1)O(1) 使用常数级的空间。

    上次更新: 2023/10/15