这是我参与8月更文挑战的第3天,活动详情查看:8月更文挑战
JZ50数组中重复的数字
描述
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任一一个重复的数字。 例如,如果输入长度为7的数组[2,3,1,0,2,5,3],那么对应的输出是2或者3。存在不合法的输入的话输出-1
示例1
输入:
[2,3,1,0,2,5,3]
返回值:
2
说明:
2或3都是对的
解题
1、set保存数组中的值
用空间换时间。遍历的同时判断set中是否有当前元素,如果有则直接返回,如果没有则保存到set集合中
/**
* 使用set保存数值
* <p>
* 运行时间:77ms
* 超过18.25% 用Java提交的代码
* 占用内存:15408KB
* 超过1.02%用Java提交的代码
* <p>
* 时间复杂度: n
* 空间复杂度: n
*
* @param numbers int整型一维数组
* @return int整型
*/
public int duplicate1(int[] numbers) {
if (numbers.length == 0) {
return -1;
}
HashSet<Integer> set = new HashSet<>(16);
for (int number : numbers) {
if (set.contains(number)) {
return number;
} else {
set.add(number);
}
}
return -1;
}
2、暴力双层for循环
- 不建议使用
/**
* 双层for循环
* 运行时间:79ms
* 超过14.57% 用Java提交的代码
* 占用内存:14384KB
* 超过28.72%用Java提交的代码
* <p>
* 时间复杂度n^2
*
* @param numbers
* @return
*/
public int duplicate2(int[] numbers) {
//采用双层for循环,判断是否有重复的
if (numbers.length == 0) {
return -1;
}
for (int i = 0; i < numbers.length; i++) {
for (int j = i + 1; j < numbers.length; j++) {
if (numbers[i] == numbers[j]) {
return numbers[i];
}
}
}
return -1;
}
3、排序+遍历
- 先排序
- 遍历数组中的元素,判断相邻元素是否相等
/**
* 可以先排序,然后遍历,如果相邻两个元素相等,则返回
* <p>
* 运行时间:108ms
* 超过2.64% 用Java提交的代码
* 占用内存:13952KB
* 超过32.13%用Java提交的代码
* <p>
*
* @param numbers
* @return
*/
public int duplicate3(int[] numbers) {
if (numbers.length == 0) {
return -1;
}
//先排序(针对排序算法,接下来会专门训练)
//todo 优化排序算法,使用更好的实现
for (int i = 0; i < numbers.length - 1; i++) {
for (int j = i + 1; j < numbers.length; j++) {
int temp = 0;
if (numbers[i] > numbers[j]) {
temp = numbers[i];
numbers[i] = numbers[j];
numbers[j] = temp;
}
}
}
//循环数组,判断相邻的两个元素是否有重复的元素
for (int i = 0; i < numbers.length - 1; i++) {
if (numbers[i] == numbers[i + 1]) {
return numbers[i];
}
}
return -1;
}
4、原地hash
理解:由题意 在一个长度为n的数组里的所有数字都在0到n-1的范围内遍历数组中的元素,所以可以将数组中的值放到与其值相等的下标位置,放的过程中与该位置上已存在的值比较,如果相等则直接返回,如果不相等,则相互替换
- 遍历数组
- 判断当前元素的值和下标为该值的元素值是否相等
- 不开辟新的数组,在原数组上操作
/**
* 由题意 在一个长度为n的数组里的所有数字都在0到n-1的范围内
* 将数组中的各元素放到与元素值相对应的下标上,如果该下标的值与该元素的值相等,则有重复值
* <p>
* 运行时间:62ms
* 超过49.10% 用Java提交的代码
* 占用内存:14244KB
* 超过30.32%用Java提交的代码
*
* @param numbers
* @return
*/
public static int duplicate4(int[] numbers) {
if (numbers.length == 0) {
return -1;
}
for (int i = 0; i < numbers.length; i++) {
if (i == numbers[i]) {
continue;
}
if (numbers[i] == numbers[numbers[i]]) {
return numbers[i];
} else {
int temp = numbers[numbers[i]];
numbers[numbers[i]] = numbers[i];
numbers[i] = temp;
i--;//如果i不等于number[i],则需要i--,直到将number[i] 放到 number[number[i]]位置上
}
}
return -1;
}
5、开辟新数组保存次数
- 开辟新的数组保存数组中值出现的次数
- 数组中值出现次数保存在和值相同的下标处
/**
* 1.开启一个新的数组来记录原数组中元素出现的次数
* 2.原数组值对应的新数组下标
* <p>
* 运行时间:60ms
* 超过54.44% 用Java提交的代码
* 占用内存:14068KB
* 超过31.53%用Java提交的代码
* <p>
*
* @param numbers
* @return
*/
public static int duplicate5(int[] numbers) {
int[] count = new int[numbers.length];
for (int i = 0; i < numbers.length; i++) {
if (count[numbers[i]] > 0) {
return numbers[i];
} else {
count[numbers[i]]++;
}
}
return -1;
}