1.leetcode原题链接:. - 力扣(LeetCode)
2.题目描述
给定一个包含 n + 1
个整数的数组 nums
,其数字都在 [1, n]
范围内(包括 1
和 n
),可知至少存在一个重复的整数。
假设 nums
只有 一个重复的整数 ,返回 这个重复的数 。
你设计的解决方案必须 不修改 数组 nums
且只用常量级 O(1)
的额外空间。
示例 1:
输入:nums = [1,3,4,2,2] 输出:2
示例 2:
输入:nums = [3,1,3,4,2] 输出:3
示例 3 :
输入:nums = [3,3,3,3,3] 输出:3
提示:
1 <= n <= 105
nums.length == n + 1
1 <= nums[i] <= n
nums
中 只有一个整数 出现 两次或多次 ,其余整数均只出现 一次
3.实现方法
思路:使用快慢指针
把 nums 看成是顺序存储的链表,nums 中每个元素的值是下一个链表节点的地址,如果 nums 有重复值,说明链表存在环,本问题就转化为了找链表中环的入口节点,因此可以用快慢指针解决。
比如数组
[3,2,1,4,8,6,2]
保存为
[0,1,2,3,4,5,6]
[3,2,1,4,8,6,2]
整体思路如下:
第一阶段,寻找环中的节点
a) 初始时,都指向链表第一个节点 nums[0];
b) 慢指针每次走一步,快指针走两步;
c) 如果有环,那么快指针一定会再次追上慢指针;相遇时,相遇节点必在环中
第二阶段,寻找环的入口节点(重复的地址值)
d) 重新定义两个指针,让 before,after 分别指向链表开始节点,相遇节点
e) before 与 after 相遇时,相遇点就是环的入口节点
第二次相遇时,应该有:
慢指针总路程 = 环外 0 到入口 + 环内入口到相遇点 (可能还有 + 环内 m 圈)
快指针总路程 = 环外 0 到入口 + 环内入口到相遇点 + 环内 n 圈
并且,快指针总路程是慢指针的 2 倍。所以:
环内 n-m 圈 = 环外 0 到入口 + 环内入口到相遇点。
把环内项移到同一边,就有:
环内相遇点到入口 + 环内 n-m-1 圈 = 环外 0 到入口
所以,从环外 0 开始,和从相遇点开始,走同样多的步数之后,一定可以在
入口处相遇。所以第二阶段的相遇点,就是环的入口,也就是重复的元素。
//快慢指针
public int findDuplicate(int[] nums) {//定义快慢指针int slow=0;int fast=0;//1. 寻找环内的相遇点do{// 快指针一次走两步,慢指针一次走一步slow=nums[slow];fast=nums[nums[fast]];}while(slow!=fast);//循环结束,slow和fast相等,都是相遇点//2. 寻找环的入口点// 另外定义两个指针,固定间距int before=0;int after=slow;while(before!=after){before=nums[before];after=nums[after];}// 循环结束,相遇点就是环的入口点,也就是重复元素return before;
}