287.寻找重复的数
思路
- 本题设置的条件很特殊 ,解法反常规,用时间换空间
- 我也只能想到哈希解法 该解法还不符合要求2
- 参考各种题解 收集以下解法(不局限于题设要求)
哈希
/**
* 使用HashMap 2ms 但不能满足 要求2
*/
public static int findDuplicate(int[] nums) {
int ans=0;
Map<Integer,Integer> map=new HashMap<>();
for (int num : nums) {
if (map.containsKey(num)) {
return num;
} else {
map.put(num, 1);
}
}
return ans
更改原数组
/**
* 2ms 但不满足要求 1
*
* 非重复的数字只会改一次正数变成负数,之后就不会访问了,
* 重复的数字会在第二次索引看到之前变为负数的值
*/
public int findDuplicate3(int[] nums){
for(int i=0;i<nums.length;i++){
if(nums[Math.abs(nums[i])]<0) return Math.abs(nums[i]);
nums[Math.abs(nums[i])]=-nums[Math.abs(nums[i])];
}
return -1;
}
二分法(抽屉原理)
原文思路参考:
liweiwei1419:使用二分法查找一个有范围的整数(结合抽屉原理)
/*
* 2ms
*
*/
public int findDuplicate(int[] nums) {
int len = nums.length;
int left = 1;
int right = len - 1;
while (left < right) {
// 在 Java 里可以这么用,当 left + right 溢出的时候,无符号右移保证结果依然正确
int mid = (left + right + 1) >>> 1;
int cnt = 0;
for (int num : nums) {
if (num < mid) {
cnt += 1;
}
}
// 根据抽屉原理,严格小于 4 的数的个数如果大于等于 4 个,
// 此时重复元素一定出现在 [1, 3] 区间里
if (cnt >= mid) {
// 重复的元素一定出现在 [left, mid - 1] 区间里
right = mid - 1;
} else {
// if 分析正确了以后,else 搜索的区间就是 if 的反面
// [mid, right]
// 注意:此时需要调整中位数的取法为上取整
left = mid;
}
}
return left;
}
- 时间复杂度:O(N log N),二分法的时间复杂度为 O(logN),在二分法的内部,执 行了一次 for 循环,时间复杂度为 O(N),故时间复杂度为 O(N logN)。
- 空间复杂度:O(1)
快慢指针
题解参考
/*
*0ms
*时间复杂度O(n)
*空间复杂度O(1)
*/
class Solution {
public int findDuplicate(int[] nums) {
int slow = 0, fast = 0;
do {
slow = nums[slow];
fast = nums[nums[fast]];
} while (slow != fast);
slow = 0;
while (slow != fast) {
slow = nums[slow];
fast = nums[fast];
}
return slow;
}
}
题解参考
class Solution {
public int findDuplicate(int[] nums) {
if(nums.length <= 2){
return nums[0];
}
//初始化i,j不应该为0.应该是指向第一个index与nums[index]不相等的位置.
int i = 0,j = 0;
for(int index = 0; index<nums.length; index++){
if(index != nums[index]){
i = index;
j = index;
break;
}
}
while(true){
i = nums[i];
j = nums[nums[j]];
if(i == j) break;
}
i = 0;
while(true){
i = nums[i];
j = nums[j];
if(i == j) break;
}
return i;
}
}
小结
- 第一次了解抽屉原理,惊叹二分法的新用法,当然题解还需好好理解。