-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Expand file tree
/
Copy pathselectk.go
More file actions
96 lines (84 loc) · 1.96 KB
/
selectk.go
File metadata and controls
96 lines (84 loc) · 1.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
package search
import (
"math/bits"
"sort"
)
// SelectK returns the k-th largest element in array.
//
// Time complexity is expected O(n) thanks to quickselect partitioning.
// A depth limit is applied and falls back to sorting the narrowed range,
// which guarantees O(n log n) worst-case behavior.
//
// The function mutates the input slice in-place.
func SelectK(array []int, k int) (int, error) {
n := len(array)
if n == 0 || k < 1 || k > n {
return -1, ErrNotFound
}
// k-th largest -> index in zero-based ascending order.
idx := n - k
return selectK(array, idx), nil
}
// selectK returns the element that would appear at idx in sorted order.
func selectK(array []int, idx int) int {
l, r := 0, len(array)
depthLimit := 2 * bits.Len(uint(len(array)))
for r-l > 1 {
if depthLimit == 0 {
sort.Ints(array[l:r])
return array[idx]
}
depthLimit--
leftPivot, rightPivot := partition3(array, l, r)
switch {
case idx < leftPivot:
r = leftPivot
case idx >= rightPivot:
l = rightPivot
default:
return array[idx]
}
}
return array[l]
}
// partition3 applies a Dutch National Flag partition around a pivot and
// returns [leftPivot, rightPivot), the range that equals the pivot.
func partition3(array []int, l, r int) (leftPivot, rightPivot int) {
pivotIdx := medianOfThreeIndex(array, l, l+(r-l)/2, r-1)
pivot := array[pivotIdx]
array[l], array[pivotIdx] = array[pivotIdx], array[l]
lt, i, gt := l, l+1, r
for i < gt {
switch {
case array[i] < pivot:
lt++
array[i], array[lt] = array[lt], array[i]
i++
case array[i] > pivot:
gt--
array[i], array[gt] = array[gt], array[i]
default:
i++
}
}
array[l], array[lt] = array[lt], array[l]
return lt, gt
}
func medianOfThreeIndex(array []int, a, b, c int) int {
if array[a] < array[b] {
if array[b] < array[c] {
return b
}
if array[a] < array[c] {
return c
}
return a
}
if array[a] < array[c] {
return a
}
if array[b] < array[c] {
return c
}
return b
}