题目:
题解:
1. 题解一:二分查找
1.1 解释一:
1.2 解释二:
1.3 解释三:
2. 题解二:快慢指针
2.1 解释一:
快慢指针思想, fast 和 slow 是指针, nums[slow] 表示取指针对应的元素
注意 nums 数组中的数字都是在 1 到 n 之间的(在数组中进行游走不会越界),
因为有重复数字的出现, 所以这个游走必然是成环的, 环的入口就是重复的元素,
即按照寻找链表环入口的思路来做
快慢指针,时间O(n),空间O(1)
把数组看作链表,nums[i]表示第i个节点指向第nums[i]个节点
然后问题就变成了找一个有环的链表中环的起始节点
2.2 解释二:
2.3 解释三:
代码:
1. 代码一:二分查找
public class code287 {
// 方法1:二分查找
public static 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;
int count = 0;
for(int i = 0; i < len; i++)
{
if(nums[i] <= mid)
{
count++;
}
}
// 根据抽屉原理,小于等于 4 的个数如果严格大于 4 个
// 此时重复元素一定出现在 [1, 4] 区间里
if(count > mid)
{
right = mid; // 重复元素位于区间 [left, mid]
}
else
{
left = mid + 1; // if 分析正确了以后,else 搜索的区间就是 if 的反面,[mid + 1, right]
}
}
return left;
}
public static void main(String[] args) {
int nums1[] = { 1, 3, 4, 2, 2 };
int res1 = findDuplicate(nums1);
System.out.println(res1);
int nums2[] = { 3, 1, 3, 4, 2 };
int res2 = findDuplicate(nums2);
System.out.println(res2);
}
}
2. 代码二:快慢指针
public class code287 {
public static int findDuplicate(int[] nums) {
int slow = 0;
int fast = 0;
slow = nums[slow];
fast = nums[nums[fast]];
// 第一步找到相遇点
// 慢指针走一步nums[slow];
// 快指针走两步nums[nums[fast]]
while(fast != slow)
{
slow = nums[slow];
fast = nums[nums[fast]];
}
// 快指针归位(0)
fast = 0;
// 第二步找到环入口
// 快指针和慢指针以相同步调行走,并最终在环入口相遇
while(fast != slow)
{
slow = nums[slow];
fast = nums[fast];
}
return fast;
}
public static void main(String[] args) {
int nums1[] = { 1, 3, 4, 2, 2 };
int res1 = findDuplicate(nums1);
System.out.println(res1);
int nums2[] = { 3, 1, 3, 4, 2 };
int res2 = findDuplicate(nums2);
System.out.println(res2);
}
}