剑指Offer 3. 数组中重复的数字
题目链接
题目描述
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
Input:
{2, 3, 1, 0, 2, 5, 3}
Output:
2或3
解题思路
1. 计数法
一看这个题要求重复的数字,首先想到的就是用计数法,找到一个个数大于1的就可以返回结果了。
class Solution {
public int findRepeatNumber(int[] nums) {
if(nums == null || nums.length == 0) return -1;
int[] count = new int[nums.length];
for(int i = 0; i < nums.length; i ++){
count[nums[i]] ++;
if(count[nums[i]] > 1) return nums[i];
}
return -1;
}
}
这样做时间复杂度和空间复杂度都是O(N),一旦要求空间复杂度是O(1)这样就行不通了、、、
2. 抽屉原理
抽屉原理指的是n个元素,n-1个抽屉,编号为i的抽屉放一个对应的元素i,一定有其中一个抽屉x需要放两个元素x,所以我们要找的这个重复元素就是x。
所以这种数组元素在 [0, n-1] 范围内的问题,可以将值为 i 的元素调整到第 i 个位置上进行求解。本题要求找出重复的数字,因此在调整过程中,如果第 i 位置上已经有一个值为 i 的元素,就可以知道 i 值重复。
以 (2, 3, 1, 0, 2, 5, 3) 为例,遍历到位置 4 时,该位置上的数为 2,但是第 2 个位置上已经有一个 2 的值了,因此可以知道 2 重复:
class Solution {
public int findRepeatNumber(int[] nums) {
if(nums == null || nums.length == 0) return -1;//判断一下非法输入
for(int i = 0; i < nums.length; i ++){
while(nums[i] != i){//只要当前位置数字和当前位置不匹配,就要执行,直到找到当前位置应该放的数字
if(nums[nums[i]] == nums[i]) return nums[i];//如果当前数字应该放的位置上已经有了对的数字,就返回结果
else{ //否则就交换
int tmp = nums[i];
nums[i] = nums[tmp];
nums[tmp] = tmp;
}
}
}
return -1;
}
}