Skip to content

Commit 09c6e47

Browse files
committed
Update 0215 solution
1 parent 98c5414 commit 09c6e47

File tree

3 files changed

+107
-44
lines changed

3 files changed

+107
-44
lines changed
Lines changed: 50 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package leetcode
22

3-
import "sort"
3+
import (
4+
"math/rand"
5+
"sort"
6+
)
47

58
// 解法一 排序,排序的方法反而速度是最快的
69
func findKthLargest1(nums []int, k int) int {
@@ -9,36 +12,62 @@ func findKthLargest1(nums []int, k int) int {
912
}
1013

1114
// 解法二 这个方法的理论依据是 partition 得到的点的下标就是最终排序之后的下标,根据这个下标,我们可以判断第 K 大的数在哪里
15+
// 时间复杂度 O(n),空间复杂度 O(log n),最坏时间复杂度为 O(n^2),空间复杂度 O(n)
1216
func findKthLargest(nums []int, k int) int {
13-
if len(nums) == 0 {
14-
return 0
15-
}
16-
return selection(nums, 0, len(nums)-1, len(nums)-k)
17+
m := len(nums) - k + 1 // mth smallest, from 1..len(nums)
18+
return selectSmallest(nums, 0, len(nums)-1, m)
1719
}
1820

19-
func selection(arr []int, l, r, k int) int {
20-
if l == r {
21-
return arr[l]
21+
func selectSmallest(nums []int, l, r, i int) int {
22+
if l >= r {
23+
return nums[l]
24+
}
25+
q := partition(nums, l, r)
26+
k := q - l + 1
27+
if k == i {
28+
return nums[q]
2229
}
23-
p := partition164(arr, l, r)
24-
if k == p {
25-
return arr[p]
26-
} else if k < p {
27-
return selection(arr, l, p-1, k)
30+
if i < k {
31+
return selectSmallest(nums, l, q-1, i)
2832
} else {
29-
return selection(arr, p+1, r, k)
33+
return selectSmallest(nums, q+1, r, i-k)
3034
}
3135
}
3236

33-
func partition164(a []int, lo, hi int) int {
34-
pivot := a[hi]
35-
i := lo - 1
36-
for j := lo; j < hi; j++ {
37-
if a[j] < pivot {
37+
func partition(nums []int, l, r int) int {
38+
k := l + rand.Intn(r-l+1) // 此处为优化,使得时间复杂度期望降为 O(n),最坏时间复杂度为 O(n^2)
39+
nums[k], nums[r] = nums[r], nums[k]
40+
i := l - 1
41+
// nums[l..i] <= nums[r]
42+
// nums[i+1..j-1] > nums[r]
43+
for j := l; j < r; j++ {
44+
if nums[j] <= nums[r] {
3845
i++
39-
a[j], a[i] = a[i], a[j]
46+
nums[i], nums[j] = nums[j], nums[i]
4047
}
4148
}
42-
a[i+1], a[hi] = a[hi], a[i+1]
49+
nums[i+1], nums[r] = nums[r], nums[i+1]
4350
return i + 1
4451
}
52+
53+
// 扩展题 剑指 Offer 40. 最小的 k 个数
54+
func getLeastNumbers(arr []int, k int) []int {
55+
return selectSmallest1(arr, 0, len(arr)-1, k)[:k]
56+
}
57+
58+
// 和 selectSmallest 实现完全一致,只是返回值不用再截取了,直接返回 nums 即可
59+
func selectSmallest1(nums []int, l, r, i int) []int {
60+
if l >= r {
61+
return nums
62+
}
63+
q := partition(nums, l, r)
64+
k := q - l + 1
65+
if k == i {
66+
return nums
67+
}
68+
if i < k {
69+
return selectSmallest1(nums, l, q-1, i)
70+
} else {
71+
return selectSmallest1(nums, q+1, r, i-k)
72+
}
73+
}

leetcode/0215.Kth-Largest-Element-in-an-Array/215. Kth Largest Element in an Array_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ func Test_Problem215(t *testing.T) {
2727

2828
qs := []question215{
2929

30+
{
31+
para215{[]int{3, 2, 1}, 2},
32+
ans215{2},
33+
},
34+
3035
{
3136
para215{[]int{3, 2, 1, 5, 6, 4}, 2},
3237
ans215{5},

website/content/ChapterFour/0200~0299/0215.Kth-Largest-Element-in-an-Array.md

Lines changed: 52 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ You may assume k is always valid, 1 ≤ k ≤ array's length.
3333

3434
## 解题思路
3535

36-
在快排的 partition 操作中,每次 partition 操作结束都会返回一个点,这个标定点的下标和最终排序之后有序数组中这个元素所在的下标是一致的。利用这个特性,我们可以不断的划分数组区间,最终找到第 K 大的元素。执行一次 partition 操作以后,如果这个元素的下标比 K 小,那么接着就在后边的区间继续执行 partition 操作;如果这个元素的下标比 K 大,那么就在左边的区间继续执行 partition 操作;如果相等就直接输出这个下标对应的数组元素即可。
37-
36+
- 在快排的 partition 操作中,每次 partition 操作结束都会返回一个点,这个标定点的下标和最终排序之后有序数组中这个元素所在的下标是一致的。利用这个特性,我们可以不断的划分数组区间,最终找到第 K 大的元素。执行一次 partition 操作以后,如果这个元素的下标比 K 小,那么接着就在后边的区间继续执行 partition 操作;如果这个元素的下标比 K 大,那么就在左边的区间继续执行 partition 操作;如果相等就直接输出这个下标对应的数组元素即可。
37+
- 快排的思路实现的算法时间复杂度为 O(n),空间复杂度为 O(logn)。由于证明过程很繁琐,所以不再这里展开讲。具体证明可以参考《算法导论》第 9 章第 2 小节。
3838

3939

4040
## 代码
@@ -43,7 +43,10 @@ You may assume k is always valid, 1 ≤ k ≤ array's length.
4343

4444
package leetcode
4545

46-
import "sort"
46+
import (
47+
"math/rand"
48+
"sort"
49+
)
4750

4851
// 解法一 排序,排序的方法反而速度是最快的
4952
func findKthLargest1(nums []int, k int) int {
@@ -52,40 +55,66 @@ func findKthLargest1(nums []int, k int) int {
5255
}
5356

5457
// 解法二 这个方法的理论依据是 partition 得到的点的下标就是最终排序之后的下标,根据这个下标,我们可以判断第 K 大的数在哪里
58+
// 时间复杂度 O(n),空间复杂度 O(log n),最坏时间复杂度为 O(n^2),空间复杂度 O(n)
5559
func findKthLargest(nums []int, k int) int {
56-
if len(nums) == 0 {
57-
return 0
58-
}
59-
return selection(nums, 0, len(nums)-1, len(nums)-k)
60+
m := len(nums) - k + 1 // mth smallest, from 1..len(nums)
61+
return selectSmallest(nums, 0, len(nums)-1, m)
6062
}
6163

62-
func selection(arr []int, l, r, k int) int {
63-
if l == r {
64-
return arr[l]
64+
func selectSmallest(nums []int, l, r, i int) int {
65+
if l >= r {
66+
return nums[l]
67+
}
68+
q := partition(nums, l, r)
69+
k := q - l + 1
70+
if k == i {
71+
return nums[q]
6572
}
66-
p := partition164(arr, l, r)
67-
if k == p {
68-
return arr[p]
69-
} else if k < p {
70-
return selection(arr, l, p-1, k)
73+
if i < k {
74+
return selectSmallest(nums, l, q-1, i)
7175
} else {
72-
return selection(arr, p+1, r, k)
76+
return selectSmallest(nums, q+1, r, i-k)
7377
}
7478
}
7579

76-
func partition164(a []int, lo, hi int) int {
77-
pivot := a[hi]
78-
i := lo - 1
79-
for j := lo; j < hi; j++ {
80-
if a[j] < pivot {
80+
func partition(nums []int, l, r int) int {
81+
k := l + rand.Intn(r-l+1) // 此处为优化,使得时间复杂度期望降为 O(n),最坏时间复杂度为 O(n^2)
82+
nums[k], nums[r] = nums[r], nums[k]
83+
i := l - 1
84+
// nums[l..i] <= nums[r]
85+
// nums[i+1..j-1] > nums[r]
86+
for j := l; j < r; j++ {
87+
if nums[j] <= nums[r] {
8188
i++
82-
a[j], a[i] = a[i], a[j]
89+
nums[i], nums[j] = nums[j], nums[i]
8390
}
8491
}
85-
a[i+1], a[hi] = a[hi], a[i+1]
92+
nums[i+1], nums[r] = nums[r], nums[i+1]
8693
return i + 1
8794
}
8895

96+
// 扩展题 剑指 Offer 40. 最小的 k 个数
97+
func getLeastNumbers(arr []int, k int) []int {
98+
return selectSmallest1(arr, 0, len(arr)-1, k)[:k]
99+
}
100+
101+
// 和 selectSmallest 实现完全一致,只是返回值不用再截取了,直接返回 nums 即可
102+
func selectSmallest1(nums []int, l, r, i int) []int {
103+
if l >= r {
104+
return nums
105+
}
106+
q := partition(nums, l, r)
107+
k := q - l + 1
108+
if k == i {
109+
return nums
110+
}
111+
if i < k {
112+
return selectSmallest1(nums, l, q-1, i)
113+
} else {
114+
return selectSmallest1(nums, q+1, r, i-k)
115+
}
116+
}
117+
89118
```
90119

91120

0 commit comments

Comments
 (0)