文章目录
287. 寻找重复数
https://leetcode-cn.com/problems/find-the-duplicate-number/
思路1: 遍历时候把数据使用hashmap存储下来
- 时间复杂度:O(n)
- 空间复杂度:O(n)
思路2: 遍历时候把数据使用hashset存储下来
- 时间复杂度:O(n)
- 空间复杂度:O(n)
思路3: 先排好序,然后遍历,如果当前数等于下一个数,则找到重复数字
- 时间复杂度:O(nlogn)
- 空间复杂度:O(1)
思路4: 遍历1-n,然后查找数组中比遍历的当前数字i小的数字有几个,假设个数大于i个,则说明就是重复数字就是i
思路5: 思路4优化:遍历1-n的时候,不使用逐个遍历,而是二分查找。
- 这里终止条件和思路4就不太一样了,需要left=right才能结束
思路6: 双指针法
具体思路参考:
https://leetcode-cn.com/problems/find-the-duplicate-number/solution/kuai-man-zhi-zhen-de-jie-shi-cong-damien_undoxie-d/
package com.shangguigu.dachang.algrithm.A02_binary;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
/**
* @author : 不二
* @date : 2022/4/6-下午10:30
* @desc : https://leetcode-cn.com/problems/find-the-duplicate-number/
**/
public class A25_findDuplicate {
public static void main(String[] args) {
int[] nums = {1, 3, 4, 2, 2};
// int duplicate = findDuplicate_v1(nums);
// int duplicate = findDuplicate_v2(nums);
int duplicate = findDuplicate_v6(nums);
System.out.println("重复的数字是:" + duplicate);
}
/**
* 第6中方法:使用快慢指针
* 这个思路也太妙了
* 具体可以看:https://leetcode-cn.com/problems/find-the-duplicate-number/solution/kuai-man-zhi-zhen-de-jie-shi-cong-damien_undoxie-d/,
* 这里解释的比较清楚
*/
public static int findDuplicate_v6(int[] nums){
int slow = 0, fast = 0;
while (true) {
slow = nums[slow];
fast = nums[nums[fast]];
// 说明在圈中相遇
if (slow == fast) {
break;
}
}
/*do {
slow = nums[slow];
fast = nums[nums[fast]];
} while (slow != fast);*/
int finder = 0;
slow = slow;
while (true) {
slow = nums[slow];
finder = nums[finder];
if (slow == finder) {
break;
}
}
return finder;
}
/**
* 第5种方法:第4种方法优化:
* 第一次遍历的时候, 不使用普通遍历,而是使用二分查找遍历,提高效率
*
* @param nums
* @return
*/
public static int findDuplicate_v5(int[] nums) {
int left = 1;
int right = nums.length-1;
while (left < right) {
int theValue = (left+right)/2;
int count = 0;
for (int j = 0; j < nums.length; j++) {
if (nums[j] <= theValue) {
count++;
}
}
if (count > theValue) {
// 这里注意不要减去1
right = theValue;
} else {
left = theValue + 1;
}
}
return left;
}
/**
* 这种的话时间复杂度就是n*n了
* 第4种方法:首先候选数字肯定是1-n种其中一个值:
* 首先 遍历1-n这n个数字:
* 没遍历到一个数字m, 从数组中计算下:比这个数字m 小于等于的数有多少个,假设有n个,那么如果 n < m,
* 那么候选数字 大于m。
* 比如说遍历到数字1, 那么数组中小于等于1的数字有1个,则不是1
* 数组中小于等于1的数字大于1个,那么重复的数字就是1了
*
*
*/
public static int findDuplicate_v4(int[] nums) {
for (int i = 1; i < nums.length; i++) {
int theValue = i;
int count = 0;
for (int j = 0; j < nums.length; j++) {
if (nums[j] <= theValue) {
count++;
}
}
if (count > theValue) {
return theValue;
}
}
return -1;
}
/**
* 第3中方法:先排好序,然后遍历,如果当前数等于下一个数,则返回当前数字
* 时间复杂度:排序O(nlogn)
* 遍历O(n)
* 结果O(nlogn)
*/
public static int findDuplicate_v3(int[] nums) {
// 内部使用的快速排序,时间复杂度:O(nlogn)
Arrays.sort(nums);
for (int i = 0; i < nums.length; i++) {
if(nums[i+1] == nums[i]){
return nums[i];
}
}
return -1;
}
/**
* 第2种方法:hashset存储下来,跟hashmap没啥区别
* 时间复杂度O(n)
* 空间复杂度O(n)
* @param nums
* @return
*/
public static int findDuplicate_v2(int[] nums) {
HashSet<Integer> set = new HashSet<>();
int result = 0;
for (int i = 0; i < nums.length; i++) {
if (set.contains(nums[i])) {
result = nums[i];
} else {
set.add(nums[i]);
}
}
return result;
}
/**
* 第1种方法:hashmap存储下来
* 时间复杂度O(n)
* 空间复杂度O(n)
* @param nums
* @return
*/
public static int findDuplicate_v1(int[] nums) {
HashMap<Integer, Integer> map = new HashMap<>();
int result = 0;
for (int i = 0; i < nums.length; i++) {
if (map.containsKey(nums[i])) {
result = nums[i];
} else {
map.put(nums[i], 1);
}
}
return result;
}
}