Skip to content

Commit 7ccf337

Browse files
committed
287.寻找重复数,双指针,二分查找
1 parent d8d23d9 commit 7ccf337

File tree

4 files changed

+98
-1
lines changed

4 files changed

+98
-1
lines changed

leetcode_Java/DoneTitle.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
239. 滑动窗口最大值
8989
279. 完全平方数
9090
283. 移动零
91+
287. 寻找重复数
9192
300. 最长上升子序列
9293
303. 区域和检索 - 数组不可变
9394
304. 二维区域和检索 - 矩阵不可变

leetcode_Java/DoneType.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@
153153
215. 数组中的第K个最大元素
154154
239. 滑动窗口最大值
155155
283. 移动零
156+
287. 寻找重复数
156157
303. 区域和检索 - 数组不可变
157158
304. 二维区域和检索 - 矩阵不可变
158159
406. 根据身高重建队列

leetcode_Java/Solution0142.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public ListNode detectCycle(ListNode head) {
5555
快慢指针:
5656
1、慢指针走一步,快指针走两步
5757
2、如果没有环,则快指针会走到空指针
58-
3、如果有环,假设在环中,以快指针为起点,快指针距离慢指针n步,那么当慢指针走n步后的位置为2n,刚好快指针也走2n步,所以会快慢指针会相遇
58+
3、如果有环,假设在环中,以快指针为起点,快指针距离慢指针n步,那么当慢指针走n步后的位置为2n,刚好快指针也走2n步,所以快慢指针会相遇
5959
4、入环口位置分析:
6060
1)设非环部分长度为a,环部分长度为b,快慢相遇时,慢指针走了s,快指针走了2s,快指针比慢指针多走了n次b,所以2s=s+nb,即s=nb
6161
2)从链表头部走到入环口需要a+nb步。即走a步到了入环口,每绕一圈b步都会回到入环口

leetcode_Java/Solution0287.java

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// 287. 寻找重复数
2+
3+
4+
/*
5+
哈希表:记录遍历过的数,如果当前数已存在于哈希表中,则该数是重复数
6+
*/
7+
class Solution {
8+
public int findDuplicate(int[] nums) {
9+
Map<Integer, Boolean> map = new HashMap<>();
10+
for (int num : nums) {
11+
if (map.getOrDefault(num, false)) {
12+
return num;
13+
}
14+
map.put(num, true);
15+
}
16+
return -1;
17+
}
18+
}
19+
20+
21+
/*
22+
快慢指针,思路同“142.环形链表II”
23+
1、如果数组中有重复的数,以数组 [1,3,4,2,2] 为例,我们将数组索引 n 和数 nums[n] 建立一个映射关系 f(n),其映射关系 n->f(n) 为:
24+
元素: 1 3 4 2 2
25+
索引: 0 1 2 3 4
26+
0->1 1->3 2->4 3->2 4->2
27+
2、我们从索引为 0 出发,根据 f(n) 计算出一个值,以这个值为新的索引,再用这个函数计算,以此类推产生一个类似链表一样的索引序列
28+
0 → 1 → 3 → 2 → 4
29+
↑_____↓
30+
3、将问题转化成环形链表
31+
1)数组中有一个重复的整数 <==> 链表中存在环
32+
2)找到数组中的重复整数 <==> 找到链表的环入口
33+
4、快慢指针走法
34+
1)慢指针走一步 slow = slow.next <==> slow = nums[slow]
35+
2)快指针走两步 fast = fast.next.next <==> fast = nums[nums[fast]]
36+
*/
37+
class Solution {
38+
public int findDuplicate(int[] nums) {
39+
int slow = 0, fast = 0;
40+
do {
41+
slow = nums[slow];
42+
fast = nums[nums[fast]];
43+
} while (slow != fast);
44+
int pre = 0;
45+
while (slow != pre) {
46+
slow = nums[slow];
47+
pre = nums[pre];
48+
}
49+
return slow;
50+
}
51+
}
52+
53+
54+
/*
55+
二分查找:
56+
1、数字在[1,n]范围内,初始化left为1,right为n。所有计算都是用数字,没用到索引
57+
2、找中间数mid,计算数组中小于等于mid的元素数量,如果数量大于mid说明重复元素在区间[left,mid],否则重复元素在区间[mid+1,right]
58+
3、循环二分查找,直到找到重复元素
59+
60+
数组:[1,3,2,5,2,4]
61+
1 2 3 4 5
62+
left mid right
63+
count=4 > mid=3
64+
======================
65+
1 2 3 4 5
66+
left mid right
67+
count=3 > mid=2
68+
======================
69+
1 2 3 4 5
70+
left/mid right
71+
count=1 !> mid=1
72+
======================
73+
1 2 3 4 5
74+
left/right
75+
*/
76+
class Solution {
77+
public int findDuplicate(int[] nums) {
78+
int left = 1, right = nums.length - 1;
79+
while (left < right) {
80+
int mid = (left + right) / 2;
81+
int count = 0;
82+
for (int num : nums) {
83+
if (num <= mid) {
84+
count++;
85+
}
86+
}
87+
if (count > mid) {
88+
right = mid;
89+
} else {
90+
left = mid + 1;
91+
}
92+
}
93+
return left;
94+
}
95+
}

0 commit comments

Comments
 (0)