2024 7/8 热热热热热热·,昨晚空调开定时,4点被热醒,全身发烫,后背湿的透透的,胶粘,起来又开了一小时,早上继续被二次热醒。看来还是得整晚开。做题啦!
1、题目描述
2、算法分析
寻找重复数。只能使用常数级空间O(1)。本来第一时间想到是用哈希表存储,但这样就不符合O(1)空间复杂度的要求。那么,该如何求解呢?想了几分钟,想不出来。看题解先。
如果数组中有重复的数,以数组 [1,3,4,2,2]
为例,我们将数组下标 n
和数 nums[n]
建立一个映射关系 f(n)
,
其映射关系 n->f(n)
为:
0->1
1->3
2->4
3->2
4->2
同样的,我们从下标为 0
出发,根据 f(n)
计算出一个值,以这个值为新的下标,再用这个函数计算,以此类推产生一个类似链表一样的序列。
0->1->3->2->4->2->4->2->……
这里 2->4
是一个循环,那么这个链表可以抽象为下图:
如果有重复数,就会产生环,那么那个环的入口元素就是我们要找的重复数。
这题正好和142题、环形链表类似,这里我贴过来:环形链表Ⅱ
3、代码
public int findDuplicate(int[] nums) {
// 初始化快慢指针
int slow = 0;
int fast = 0;
// 快慢指针开始移动,快指针每次移动两步,慢指针每次移动一步
// 慢指针移动到下个位置
// 快指针移动到下下个位置
slow = nums[slow];
fast = nums[nums[fast]];
// 当快慢指针不相遇时,继续移动
while(slow != fast){
// 慢指针继续移动
slow = nums[slow];
// 快指针继续以两倍速度移动
fast = nums[nums[fast]];
}
// 快慢指针相遇,表示找到了环的入口
// 此时,将其中一个指针重置到数组的起始位置(0),然后两个指针以相同速度移动
// 它们将在环的入口再次相遇
// 第一个指针(或者叫“寻找环入口的指针”)
int pre1 = 0;
// 第二个指针(此时与快指针相遇的慢指针)
int pre2 = slow;
// 两个指针同时移动,直到它们再次相遇
while(pre1 != pre2){
pre1 = nums[pre1];
pre2 = nums[pre2];
}
// 当两个指针再次相遇时,它们相遇的位置就是环的入口,也就是我们要找的重复数字
// 因为所有数字都是1到n之间的,而数组长度是n+1,所以环的入口必然是重复的数字
return pre1;
}
4、复杂度分析
- 时间复杂度:
O(n)
。「Floyd 判圈算法」时间复杂度为线性的时间复杂度。 - 空间复杂度:
O(1)
。我们只需要常数空间存放若干变量。
okok,再见啦!