一.题目描述
找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
二.题目解答
有三种解法:
package exercise1;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/**
* offer03
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。
数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。
请找出数组中任意一个重复的数字。
*/
public class Solution {
public int findRepeatNumber(int[] nums) {
/*哈希表
* 时间复杂度O(n),空间复杂度O(n)
* */
Set set = new HashSet();
for (int i = 0; i < nums.length; i++) {
boolean flag = set.add(nums[i]);
if(!flag){
return nums[i];
}
}
return -1;
}
public int findRepeatNumber1(int[] nums) {
/*
* 时间复杂度O(nlogn),空间复杂度O(1)
* */
Arrays.sort(nums);
for (int i = 0; i < nums.length - 1; i++) {
if(nums[i] == nums[i + 1]){
return nums[i];
}
}
return -1;
}
public int findRepeatNumber2(int[] nums) {
/*原地置换算法
* 时间复杂度O(n),空间复杂度O(1)
* */
int temp;
for (int i = 0; i < nums.length; i++) {
int actual = nums[i];
while(actual != i){
//如果i位置上的数字actual不是预期的i,那么说明下标为actual位置上的数字也有可能不是预期的actual
//(当不存在重复元素时肯定不是)
//于是交换,当出现重复元素时,交换过程中必有actual == nums[actual]相等的时刻
if(actual == nums[actual]){
return actual;
}
temp = actual;
nums[i] = nums[actual];
nums[actual] = temp;
actual = nums[i];
}
}
return -1;
}
}
注:还有一种方法是二分查找,思想是
假设10个数0-9,start=0,end=9,mid=4
* 统计0-4的5个数在10个数中出现的次数,如果0-4不存在重复,则次数是5,只需要将目标锁定在mid+1--end的数。
* 如果0-4出现重复,则次数将大于5,这时将目标锁定在start,mid中。
* 以此类推
package exercise1;
/**
* offer03,二分法找重复元素,时间复杂度O(nlogn),空间复杂度O(n)
* 缺点是不能确保100%能找出-比如统计1-2范围内count=2,我们无法确定是每个数字各出现一次还是某个数字出现了两次
*/
public class Solution1 {
public static void main(String[] args) {
int[] arr = new int[]{0, 1, 2, 0, 4, 5, 6, 7, 8, 9};
System.out.println(new Solution1().findRepeatNumber(arr));
}
public int findRepeatNumber(int[] nums) {
//0-n-1,长度n
return getDuplication(nums,nums.length);
}
private int getDuplication(int[] nums, int length) {
if(nums == null || length <= 0){
return -1;
}
int start = 0;
int end = length -1;
while(end >= start){
//运算符优先级,((end - start) >> 1)
int middle = ((end - start) >> 1) + start;
int count = getCount(nums,length,start,middle);
if(end == start){
if(count > 1){
return start;
}
}
if(count > middle - start + 1){
end = middle;
}else{
start = middle + 1;
}
}
return -1;
}
private int getCount(int[] nums, int length, int start, int end) {
if(nums == null || length <= 0){
return 0;
}
int count = 0;
for (int i = 0; i < length; i++) {
if(nums[i] >= start && nums[i] <= end){
count++;
}
}
return count;
}
}