第3题:数组中重复的数字

本文详细解析了多种查找数组中重复数字的方法,包括HashSet法、排序比较法、原地置换法和暴力法。HashSet法和原地置换法在时间和空间效率上较为优秀,适合处理大数据。文章通过实例代码解释了每种方法的实现逻辑,并分析了它们的时间和空间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考leetcode解答

1. 题目描述

在这里插入图片描述

  • 问面试官时间空间要求,再考虑具体什么方法?

2. HashSet法(重点)

  • 可以使用hashset,如果数组元素不在hashset中就add,如果已经存在就返回(即结果)。
  • 时间和空间复杂度都是O(n),多个set所以O(n)

hashset特点

  • 参考
  • 基于hashmap实现
  • 无序且唯一
  • 因为无序,所以没有索引操作,只能增强for遍历,普通for(需要索引)
  • add,remove,contains,clear,size等方法

hashset和hashmap区别

  • 参考
  • 一个实现set接口,一个map接口
  • map键值对,set无序所以只能遍历
  • map的key不重复,value可重复,不同key可以对应相同value

代码

class Solution {
    
    public int findRepeatNumber(int[] nums) {
        // 判断nums长度
        if(nums.length <= 1){
            return -1;
        }
        
        // 使用HashSet
        HashSet<Integer> set = new HashSet<>();

        // 如果set没有就添加,如果有就返回
        for(int num:nums){
            
            if(set.contains(num)){
                return num;
            }
            else{
                set.add(num);
            }
        }

        // 上面没找到就返回-1,因为nums中的数 0-n-1
        return -1;
    }
}

3. 先排序后比较

  • 对数组进行排序(手写推荐:堆排序,系统函数:sort()),此步骤时间复杂度O(nlog(n));

  • 遍历数组,查看相邻元素是否有相等的,有直接返回结果,此步骤时间复杂度O(n);

  • 复杂度分析:

    • 时间复杂度:O(nlog(n)),上面已经分析出时间复杂度
    • 空间复杂度:O(1),没有使用多余空间
class Solution {
    public int duplicate(int[] numbers) {

        sort(numbers.begin(),numbers.end());//排序
        for(int i=1;i<numbers.size();++i){//遍历
             if(numbers[i-1]==numbers[i]) return numbers[i];
        }
        return -1;
    }
}

4. 原地置换法(重点)

  • 在一个长度为 n 的数组 nums 里的所有数字都在 0 ~ n-1 的范围内 。 此说明含义:数组元素的 索引 和 值 是 一对多 的关系。
    • 如题目[2,3,1,0,2,5,3] 则,数组长度为7,数组内元素大小只能是 0 - 6,所以0-6刚好对应数组的索引,就表明数组内元素都可以放到它们值对应的索引处。
  • 因此,可遍历数组并通过交换操作,使元素的 索引 与 值 一一对应(即 nums[i] == i )。因而,就能通过索引映射对应的值,起到与字典等价的作用。

在这里插入图片描述

遍历中,第一次遇到数字 xx 时,将其交换至索引 xx 处;而当第二次遇到数字 xx 时,一定有 nums[x] = num[num[x]](即当前索引位置元素的值==该元素对应索引位置处的值),此时即可得到一组重复数字。

过程详解

  • 依据题目数组 [2,3,1,0,2,5,3]

  • 一开始数值2在数组索引0处,但它应该在索引2处,即比较当前 num[x] = x是否成立(x是数组索引,0开始),如果不成立就把当前元素和当前元素值应该在的索引位置的值交换(即数值2和2索引位置的值1交换位置)
    在这里插入图片描述

在这里插入图片描述

  • 然后继续判断当前位置是否满足假设,如下图索引0位置数值1,不满足假设(num[x] = x),就继续交换

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 然后继续判断还是不满足假设,那就继续交换,0索引值3 和 3索引值0

在这里插入图片描述

在这里插入图片描述

  • 交换完毕之后,继续判断当前是否满足假设,一看 num[0] = 0,满足假设,所以指针向后

在这里插入图片描述
在这里插入图片描述

  • 直到指针到索引4值2处不满足假设。

  • 索引4值2 应该与 索引2处值交换,但是发现它们值相等,此时找到重复的数,直接返回即可!
    在这里插入图片描述
    在这里插入图片描述

  • 时间复杂度 O(N) : 遍历数组使用 O(N) ,每轮遍历的判断和交换操作使用 O(1) 。

  • 空间复杂度 O(1) : 使用常数复杂度的额外空间(交换的temp空间)。

class Solution {
    public int findRepeatNumber(int[] nums) {
        if (nums.length <= 1) {
            return -1;
        }

        // 索引值,如果当前索引不等于索引处的值就判断,相等就索引+1,继续向前找
        for (int i = 0; i < nums.length; ++i) {
            while (nums[i] != i) {
            
                // 发现重复元素,此时想找到的重复2那张图
                if (nums[i] == nums[nums[i]]) {
                    return nums[i];
                }
                // 不重复就交换位置,此时想第一次交换的图
                int temp = nums[i];
                nums[i] = nums[temp];
                nums[temp] = temp;
            }
        }
        return -1;
    }
}

5. 暴力法(了解)

  • 解题思路:第一层循环依次选取一个锚点数字,第二层循环遍历剩下的数字,当找到相同的两个数字,退出循环,返回正确的结果。

  • 复杂度分析:

    • 时间复杂度:O(n^2),两层循环
    • 空间复杂度:O(1)
class Solution {
    public int findRepeatNumber(int[] numbers) {

        for(int i=0;i<numbers.size()-1;i++){//第一层循环,选择锚点数字
           for(int j=i+1;j<numbers.size();j++){//第二层,匹配两数是否相等
               if(numbers[i]==numbers[j]) return numbers[i];
           }
        }
        return -1;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值