题目描述
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。
思路1:逐个扫描,每个数都扫描一轮。时间复杂度是O(n^2)
public class Solution {
// 返回任意重复的一个值,赋值duplication[0],这是题目要求
public boolean duplicate(int numbers[],int length,int [] duplication) {
for (int i = 0; i < length; i++) {
for (int j = i + 1; j < length; j++) {
if (numbers[j] == numbers[i]) {
duplication[0] = numbers[i];
return true;
}
}
}
return false;
}
}思路2:利用排序,再从排序好的数组中找,排序好后只用扫一轮就可以,排序时间复杂度一般能优化到O(nlogn)
import java.util.Arrays;
public class Solution {
public boolean duplicate(int numbers[],int length,int [] duplication) {
if(numbers == null || length == 0){
return false;
}
Arrays.sort(numbers);
for(int i=0;i<length-1;i++){
if(numbers[i] == numbers[i+1]){
duplication[0] = numbers[i];
return true;
}
}
return false;
}
}思路3:利用哈希表或辅助数组,空间换时间,空间复杂度是O(n),时间复杂度O(1)。当哈希表添加元素相同时,说明重复了(或者使用Hashmap记录次数,最后次数大于1的就是重复的)。自然,不用哈希表,可以用一个辅助数组来记录对应元素的角标,也是可以的(同样也可以用数组来记录次数)
import java.util.HashSet;
import java.util.Set;
public class Solution {
public boolean duplicate(int numbers[],int length,int [] duplication) {
Set<Integer> set = new HashSet<>();
for(int i =0 ;i<length;i++){
if(set.contains(numbers[i])){
duplication[0] = numbers[i];
return true;
}else{
set.add(numbers[i]);
}
}
return false;
}
}
//使用辅助数组的写法
public class Solution {
public boolean duplicate(int numbers[],int length,int [] duplication) {
int[] arr = new int[length];
for(int j = 0;j<length;j++){
arr[j] = -1; //辅助数组赋初值,只要不在1到n-1即可,否则在后续判断时会出错
}
for(int i =0 ;i<length;i++){
if(arr[numbers[i]] == numbers[i]){
duplication[0] = numbers[i];
return true;
}else{
arr[numbers[i]] = numbers[i];
}
}
return false;
}
}
//显然用来记录次数的辅助数组,不用赋初值 只要次数超过1就可以返回了,++的条件时次数为零
public class Solution {
public boolean duplicate(int numbers[],int length,int [] duplication) {
int[] arr = new int [length];
for(int i = 0; i < length; i++){
if(arr[numbers[i]] == 0){
arr[numbers[i]] ++;
}else{
duplication[0] = numbers[i];
return true;
}
}
return false;
}
}思路4:这不是一个普通的具有重复数字的数组,利用条件--长度为n,数的范围1到n-1,因此如果每个数字都不重复时,排完序的数组中的元素值和该元素的索引相同,而一旦有重复的数,那就会出现,元素和角标不相同的情况,基于这个条件,构建思路4,也叫重排数组(其实和哈希表想法相似,当同一个位置元素出现多次时,说明重复了)这个想法需要不断交换对应的元素。
比如{4,1,3,1,4} ,将索引为0的4移动到索引为4的元素交换,让4归位,但是移动时发现索引为4的元素已经是4了,就说明出现了两次。
public class Solution {
public boolean duplicate(int numbers[],int length,int [] duplication) {
if(numbers == null || length <= 0) {
return false;
}
for(int i = 0; i < length; i++) {
while(numbers[i] != i) {
if(numbers[i] == numbers[numbers[i]]) {
duplication[0] = numbers[i];
return true;
}
int temp = numbers[i];
numbers[i] = numbers[temp];
numbers[temp] = temp;
}
}
return false;
}
}很显然,思路4打乱了原数组的顺序,但是空间复杂度较低
附:在剑指offer书中,本题还有一个附加题,那就是不允许打乱数组顺序,并且题目改为了长度为n+1,所有数字是1到n的范围。其余条件不变。显然之前的辅助数组的方法是可以的,如果辅助数组也不让用了。
书给出的解答方式是二分法:把1到n的数字从中间数字m分成两部分,若前一半1到m的数字数目超过m个,说明重复数字在前一半区间,否则,在后半区间m+1到n。每次在区间中都一分为二,直到找到重复数字。
整个时间复杂度是O(nlogn) 空间复杂度O(1).



京公网安备 11010502036488号