寻找重复数
给定一个包含 n + 1 个整数的数组 nums ,其数字都在 [1, n] 范围内(包括 1 和 n),可知至少存在一个重复的整数。
假设 nums 只有 一个重复的整数 ,返回 这个重复的数 。
你设计的解决方案必须 不修改 数组 nums 且只用常量级 O(1) 的额外空间。
示例1:
输入:nums = [1,3,4,2,2]
输出:2
解题思路
如何将输入的数组看作为链表?如果能转化成链表,则可以使用 Leetcode 142. 环形链表 II的思想,找到环的起点即可。
可以使用数学函数思想,定义一个f(x)函数,
输入为:
- [1,3,4,2]
则有函数的映射关系 x->f(x)为: - 0->1
- 1->3
- 2->4
- 3->2
- 我们从下标为 0 出发,根据 f(x)计算结果值为(类似高中函数定义,令发y=f(x),有x值和y值对应,这里就是x从0开始,数组元素作为y和x组成对应关系,即函数):
- 0->1->3->2->4->null
那如果数组有重复元素,假设数组为:
- [1,3,4,2,2]
对应f(x)函数一定会产生多对一的映射(即有2个即以上的x对应一个y值)
对应到链表上,则如下:
其映射关系 x->f(x) 为:
- 0->1
- 1->3
- 2->4
- 3->2
- 4->2
同样的,我们从下标为 x= 0 出发,根据 f(x) 计算出一个值,以这个值为新的下标,再用这个函数计算,以此类推产生一个类似链表一样的序列。
0->1->3->2->4->2->4->2->……
可以发现,环的起点4即为重复元素。使用Leetcode 142. 环形链表 II思想即可解题。
java实现
public class FindDuplicate {
public int findDuplicate(int[] nums) {
int slow = nums[0];
int fast = nums[0];
// Phase 1: 检测是否存在循环,可能在环中的某个点相遇
//有重复元素一定存在环
do {
//对应f(x)的函数关系,相当于链表的slow = slow.next
slow = nums[slow];
//对应f(x)的函数关系 相当于链表fast = fast.next.next
fast = nums[nums[fast]];
} while (slow != fast);
// Phase 2: 找到循环的入口
//因为相遇点到环的入口的距离与数组起点到环的入口的距离相等
//数组起点到环的入口的距离a 一个一步a+b,一个两步,
// a+c=2(a+b) 因为相遇a=c-2b 即两步走的那个距离环的入口一定多走了a
fast = nums[0];
while (slow != fast) {
slow = nums[slow];
fast = nums[fast];
}
return slow;
}
public static void main(String[] args) {
FindDuplicate solution = new FindDuplicate();
//0->1->4->2->3
// ^ |
// | |
// ------
int[] nums = {1, 4, 3, 4, 2,5};
int result = solution.findDuplicate(nums);
System.out.println("The duplicate number is: " + result);
}
}
时间空间复杂度
- 时间复杂度为O(n)。
- 空间复杂度为O(1)。