数组中重复的数字
- 来源于《剑指offer》牛客网
1. 题目描述
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中第一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
返回描述:
如果数组中有重复的数字,函数返回true,否则返回false。
如果数组中有重复的数字,把重复的数字放到参数duplication[0]中。(ps:duplication已经初始化,可以直接赋值使用。)
2. 解题思路
1) 根据题干中的关键信息“数组中的整数取值范围为[0, n)”数组中的数字可以与数组索引一一对应,即 index = array[index] 如何使用索引值进一步定位数组中重复的数字,就是高效率解决这个问题的关键。
2)根据题目的关键问题“第一个重复的数字”可以知道要完美地解决这个问题必须按照原顺序输出第一个重复的数字。
3)根据关键信息与关键的问题可以得到的条件是:使用索引寻找数组中重复的数字时,要么不改变数字的位置,要么采取一些手段保存数字的原始位置。
3. 代码实现
3.1 根据索引交换数字和使用额外数组存储优先级
- 以交换的形式把数组中的数字放在与它大小相等的索引位置上,如果数组中没有相同的数字,那么最后数组中的数组与索引的大小都相等;如果数组中有相同的数字,那么与该值对应的索引位置必定会被访问两次
- 使用额外的数组存储优先级
- 时间复杂度O(N)
实现代码:
public boolean duplicate1(int numbers[],int length,int [] duplication) {
int i = 0, init = duplication[0];
int next[] = new int[length];
//设置优先级数组
for ( int j = 0; j < length; j++ ) {
if ( next[numbers[j]] == 0 ) {
next[numbers[j]] = j + 1;
}
}
while ( i < length ) {
//判断i是否等于numbers[i]
if ( i != numbers[i] ) {
//判断是否出现重复元素
if ( numbers[i] == numbers[numbers[i]] ) {
//根据定位数组更新duplicatin数组
if ( duplication[0] == init || next[duplication[0]] > next[numbers[i]])
duplication[0] = numbers[i];
} else {
//不符合条件——交换
swap(numbers, i, numbers[i]);
continue;
}
}
i++;
}
if ( duplication[0] != init )
return true;
return false;
}
public void swap(int numbers[], int repos, int dipos) {
int temp;
temp = numbers[repos];
numbers[repos] = numbers[dipos];
numbers[dipos] = temp;
}
3.2 附加标识法(元素大小或者负数)
- 把数组的长度作为权重(标志元素是否被访问过),数组中的元素如果大于或者等于数组长度即说明被访问过
- 构造公式:numbers[i] = (numbers[i] + 1) * length + i (可以存储数组元素的位置信息和访问情况)
- 根据构造公式进行计算
- 时间复杂度O(N)
public static boolean duplicate3(int numbers[],int length,int [] duplication) {
int index, init = duplication[0];
if (length == 0 || numbers == null) {
return false;
}
for (int i = 0; i < length; i++) {
index = numbers[i];
if (index >= length) {
index = index / length - 1;
}
if (numbers[index] >= length) {
if (duplication[0] == init || duplication[0] % length > numbers[index] % length) {
duplication[0] = numbers[index];
}
} else {
//避免0影响整个结果,0不能构造结构式
numbers[index] = (numbers[index] + 1) * length + i;
}
}
if (duplication[0] != init) {
//解析结构式
if (numbers[duplication[0] % length] >= length) {
duplication[0] = numbers[duplication[0] % length] / length - 1;
} else {
duplication[0] = numbers[duplication[0] % length];
}
return true;
} else {
return false;
}
}
}
3.3 使用HashSet(散列)
- 利用散列数据结构的特性,快速找出重复的数字
- 时间复杂度O(N)
public boolean duplicate(int numbers[],int length,int [] duplication) {
if(length == 0 || numbers==null){
return false;
}
HashSet<Integer> set = new HashSet<>();
for(int num : numbers){
if(set.contains(num)){
duplication[0] = num;
return true;
}
set.add(num);
}
return false;
}
}