一、需求
- 在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内;
- 数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次;
- 请找出数组中任意一个重复的数字。
二、暴力法
2.1 思路分析
- 利用双重for循环,两两比较,比如第1个数和第2,3,4,...,n个数比较,然后第2个数和第3,4,5,...,n个数比较,第 i 个数和第 i + 1~n个数比较,直到出现重复元素,就返回;
- 本题数组的最大长度为10^5,因此当时间复杂度为O(n^2)时,存在超时问题;
2.2 代码实现
class Solution {
public int findRepeatNumber(int[] nums) {
for(int i = 0; i < nums.length; i++) {
for(int j = i + 1; j < nums.length; j++) {
if(nums[i] == nums[j]) return nums[i];
}
}
return 0;
}
}
2.3 复杂度分析
- 时间复杂度为O(n^2);
- 空间复杂度为O(1);
三、哈希表法
3.1 思路分析
- 哈希表也有很多种,这里用HashSet比较合适,HashMap也能做,但它涉及到了键值对,在这里只需用用Set判断是否包含重复元素就好;
- 遍历数组,若Set中已经包含了某元素,那么直接返回;
3.2 代码实现
class Solution {
public int findRepeatNumber(int[] nums) {
Set<Integer> set = new HashSet<>();
for(int num : nums) {
if(!set.add(num)) {
return num;
}
}
return 0;
}
}
3.3 复杂度分析
- 时间复杂度为O(n);
- 空间复杂度为O(n);
四、排序法
4.1 思路分析
- 首先将数组进行排序,然后就遍历数组,看看相邻元素是否有重复的,如果有重复的,就返回这个元素;
4.2 代码实现
class Solution {
public int findRepeatNumber(int[] nums) {
Arrays.sort(nums);
for(int i = 1; i < nums.length; i++) {
if(nums[i] == nums[i - 1]) return nums[i];
}
return 0;
}
}
4.3 复杂度分析
- 时间复杂度为O(n);
- 空间复杂度为O(1);
五、原地置换法
5.1 思路分析
- 题目要求数组中的元素的取值范围是:0~n-1,比如数组[2,3,1,0,2,5,3],我们要将数组各元素值,安排到对应的索引上,比如数组首元素2对应的索引是0,应该把它安排到索引2上;
- 这就有了一个问题,什么时候需要安排索引呢?我们定义变量 i 来遍历数组,当 nums[i] == i时就不需要安排,当nums[i] != i 时需要安排;
- 那么另外一个问题,如何安排索引?比如现在 i = 0,此时 i != nums[i],即0 != 2,需要把 2 放到索引 2 处,故交换的是 2 和 1,2本身就是nums[i],那么1用什么表示呢,1 = nums[2] = nums[nums[i]],对,就这样,交换它们的位置;
- 遇到重复元素怎么处理?假设第一个 2 已结在索引 2 处,现在 i 已经到了第二个 2 的位置,即 i = 4,现在要判断是不是有两个 2,首先nums[i] == 2,然后另一个2 == nums[nums[i]],也就是说当nums[ i] == nums[nums[i]]时,就出现了重复元素,此时,我们返回该元素。
5.2 代码实现
class Solution {
// 原地置换法
public int findDuplicate(int[] nums) {
for (int i = 0; i < nums.length;) {
// nums[i]已在索引i处
if (nums[i] == i) {
i++;
continue;
}
// 这个判断要在前面
if (nums[i] == nums[nums[i]]) {
return nums[i];
}
// 将nums[i]置于指定索引处
int tmp = nums[i];
nums[i] = nums[tmp];
nums[tmp] = tmp;
}
return 0;
}
}
5.3 复杂度分析
- 时间复杂度为O(n);
- 空间复杂度为O(1);