剑指 Offer 03. 数组中重复的数字

246 阅读3分钟

Offer 驾到,掘友接招!我正在参与2022春招系列活动-刷题打卡任务,点击查看活动详情

一、题目描述

找出数组中重复的数字。

在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。

示例

示例1:

输入:
[2, 3, 1, 0, 2, 5, 3]
输出: 23

限制

  • 2 <= n <= 100000

难度:简单

此题为简单题,兄弟们可以重拳出击,没错,就是你想到的那种方法,简单粗暴!

二、思路分析

众所周知,力扣上,简单题是真简单,中等题有些困难,困难题看不懂答案。

题目解析

  1. 找出给定数组中重复的数字,根据题目描述,数组 nums 的长度为 n,且其中的所有数字都在 [0,1] 区间,其中含有一个或多个重复数字,且重复次数不限,找到任意一个重复数字返回。

  2. 容器判重:题目理解后,首先想到的就是遍历数组,并在遍历过程中使用容器记录每个出现的数字,如果容器中已经存在当前数字,则说明该数字重复,返回当前数字即可。

  3. 数组判重:容器记录并判断重复可以用于任意寻找重复数字的情况,而当前题目还可以使用数组判重的方式

    • 题目数组中所有数字都在 [0,1] 区间内,也就是在长度为 n 的数组索引范围内,因此可以使用长度为 n 的数组记录出现的数字,如果数字存在则对应索引位置赋值为 1;如果已经为 1,说明索引对应数字重复。
  4. 数组原地判重:元素数组和记录数组长度都是 n,因此可以考虑合二为一,在原数组上遍历同事进行操作记录,实现原地判重

    • 数组中数字均在 [0,1] 区间,可以在遍历到数字后,为其对应索引位置上的数字 +n 或 -n 作为标记,
    • 在遍历操作之前判断数字是否仍然在 [0,1] 区间内,如果不存在说明索引对应数字重复,返回索引值,
    • 由于遍历时不确定数字是否有变动,需要在操作时对所有数字进行 %n 后进行索引操作。

三、AC 代码

使用容器判重

  • 使用set容器存储元素并判重,set为不重复集合,添加元素时如果已经存在返回false,否则返回true
public int findRepeatNumber(int[] nums) {   
    Set<Integer> set = new HashSet<Integer>();
    for(int i = 0; i < nums.length; i++){
        //if(set.contains(nums[i])) return nums[i];
        if(!set.add(nums[i])) return nums[i];  
    }
    return -1; 
}

image.png

数组原地判重

  • 使用 +n 或者 -n 来标识对应位置标注为已出现过,因为所有元素都在[0,1] 区间,且可以保持原有元素,本方法使用 +n 操作,并使用 >= n 判断
public int findRepeatNumber(int[] nums) {
    int n = nums.length;
    for(int i = 0; i < n; i++){
        int cur = nums[i] % n;
        if(nums[cur] >= n) return cur;
        nums[cur] += n;
    }
    return -1;
}

image.png

四、总结

知识点

  • set 容器用于存储不重复元素,如果已经存在,则加入时返回 false,效率高
  • 对于数组中元素不超过数组索引范围的,可以使用定长数组来代替容器,甚至可以直接在原数组上操作,如果允许对原数组修改的话。

最后

阳春三月,算法刷起来!LeetCode 剑指 Offer

简单题,不需要考虑太多,开干就是了。