Leetcode——寻找重复数
Posted Yawn,
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Leetcode——寻找重复数相关的知识,希望对你有一定的参考价值。
1. 寻找重复数
因为该题要求只能用常量级的空间,所以不能直接使用暴力set查重
(1)快慢指针
- fast 和 slow 是指针, nums[slow] 表示取指针对应的元素
- 注意 nums 数组中的数字都是在 1 到 n 之间的(在数组中进行游走不会越界),
- 因为有重复数字的出现, 所以这个游走必然是成环的, 环的入口就是重复的元素,
- 即按照寻找链表环入口的思路来做(参考环形链表2)
第一次相遇时,fast总的路程是slow总的路程加上n圈环。总fast=总slow+n圈环 也就是总fast=总slow+nb,而总fast的路程还是总slow路程的两倍,所以总fast=2总slow,所以两式得出总slow=nb,总fast=2nb。所以第一次相遇的时候slow就是已经走了nb,在这里n=1
- 第一次相遇时慢指针已经走了nb步
从head结点走到入环点需要走 : a + nb, 而slow已经走了nb,那么slow再走a步就是入环点了。
- 走a+nb步一定是在环入口
class Solution {
public int findDuplicate(int[] nums) {
int fast = 0, slow = 0;
while (true) {
fast = nums[nums[fast]]; //快指针走两步 fast = fast.next.next ==> 本题 fast = nums[nums[fast]]
slow = nums[slow]; //慢指针走一步 slow = slow.next ==> 本题 slow = nums[slow]
//找到第一个相遇点 fast = 2 * slow, fast = slow + nb => slow = nb, slow + a = 入环点
if (slow == fast) {
fast = 0;
//fast回头节点开始走,恰好走a个位置到达入环点与slow + a相遇
while(nums[slow] != nums[fast]) {
fast = nums[fast];
slow = nums[slow];
}
return nums[fast];
}
}
}
}
(2)字典数组(一个萝卜一个坑)
class Solution {
public int findDuplicate(int[] nums) {
int i = 0;
while (true) {
//假如nums[i] == i + 1,说明这个坑位占的元素是对的,指针往后移
if (nums[i] == i + 1) {
i++;
}
//假如要放的坑位上的元素已经有这个元素了,说明找到重复数了,直接返回这个数字
else if (nums[i] == nums[nums[i] - 1])
return nums[i];
//其他情况,指针对应元素不在应该放的坑位中,交换到自己对应的坑位。
else swap(nums, i, nums[i] - 1);
}
}
void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
(3)二分(结合抽屉原理)
二分查找的思路是先猜一个数(有效范围 [left…right] 里位于中间的数 mid),然后统计原始数组中 小于等于 mid 的元素的个数 count:
- 如果 count严格大于 mid。根据抽屉原理,重复元素就在区间 [left…mid] 里;
- 否则,重复元素就在区间 [mid + 1…right] 里。
与绝大多数使用二分查找问题不同的是,这道题正着思考是容易的,即:思考哪边区间存在重复数是容易的,因为有抽屉原理做保证。
public class Solution {
public int findDuplicate(int[] nums) {
int len = nums.length;
int left = 1;
int right = len - 1;
while (left < right) {
int mid = left + (right - left) / 2;
int cnt = 0;
for (int num : nums) {
if (num <= mid) {
cnt += 1;
}
}
// 根据抽屉原理,小于等于 4 的个数如果严格大于 4 个,此时重复元素一定出现在 [1..4] 区间里
if (cnt > mid) {
// 重复元素位于区间 [left..mid]
right = mid;
} else {
// if 分析正确了以后,else 搜索的区间就是 if 的反面区间 [mid + 1..right]
left = mid + 1;
}
}
return left;
}
}
以上是关于Leetcode——寻找重复数的主要内容,如果未能解决你的问题,请参考以下文章