在牛客网刷的,还是跟leetcode一样非acm模式,由于急着暑期实习题量不固定,八股算法轮刷
打卡内容偏个人笔记,本人水平一般(代码随想录稀里糊涂刷了一遍),从小白开始分析(甚至会分析语法),尽量一题多解深入探究(一般ac后看看前三个题解发散下思维),希望能对你有帮助
JZ3 数组中重复的数字
描述
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组[2,3,1,0,2,5,3],那么对应的输出是2或者3。存在不合法的输入则输出-1
分析
首先暴力解法外层for循环和内层for循环匹配到一样的元素肯定可以实现
接下来由于空间复杂度O(n) ,数据范围:0≤n≤10000 ,可以用数组实现一个哈希表,扫描一遍输入数组后得到是否重复的信息,不合法的输入的话输出-1就直接在处理完逻辑后return -1,直接ac了,当然代码可以在第一个for循环里更新hash后马上判断,代码简洁且更快return(在循环里多点语句,能一个循环解决就放一起)
public int duplicate(int[] numbers) {
int[] hash = new int[10010];
for (int i = 0; i < numbers.length; i++) {
hash[numbers[i]]++;
}
for (int i = 0; i < hash.length; i++) {
if (hash[i] >= 2) {
return i;
}
}
return -1;
}
看了题解想起来可以用集合HashSet,顺便复习了范型
Set hash = new HashSet();和Set<Integer> hash = new HashSet<>();的区别:
在Java 1.5版本之前,泛型并不是Java语言的一部分,所以可以直接使用 Set
而不需要指定泛型类型。但是这种方式在Java 1.5版本之后就被认为是不推荐的,因为它会导致编译器无法进行类型检查,可能会引发类型安全问题。
Set<Integer> hash = new HashSet<>();
这行代码中通过使用泛型指定了 HashSet
中元素的类型为 Integer
。这种方式是更加安全和推荐的写法,因为编译器会进行类型检查,可以在编译时捕获类型错误。
使用HashSet的代码如下,先判断Set中是否存在,不存在就加入(if下面的代码自带else逻辑)
public int duplicate(int[] numbers) {
Set<Integer> hash = new HashSet<>();
for (int i = 0; i < numbers.length; i++) {
if (hash.contains(numbers[i])) {
return numbers[i];
}
hash.add(numbers[i]);
}
return -1;
}
看了题解发现这道题可以边遍历变=边替换,通过把不符合的交换维护numbers[i] = i,因为数组中的元素范围是 0 到 n-1,所以如果数组中没有重复数字,那么当数组排序后,数字 i 将出现在下标为 i 的位置上。如果有重复数字,则会被检测到。
- 在交换数字后,可能会出现新的数字出现在原本的位置上,所以需要在交换后重新检查该位置。
- 为了实现这一点,在交换后将当前索引 i 减一,使得下次循环重新检查当前位置。
public int duplicate(int[] numbers) {
for (int i = 0; i < numbers.length; i++) {
if (numbers[i] != i) {
if (numbers[i] == numbers[numbers[i]])
return numbers[i];
int temp = numbers[numbers[i]];
numbers[numbers[i]] = numbers[i];
numbers[i] = temp;
i--; // 遍历完0位元素以及交换完数字后,如果0位元素仍不符合数组存放原则:numbers[i] = i,那么还要重新遍历0位元素
}
}
return -1;
}