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

被折叠的 条评论
为什么被折叠?



