Skip to content

Commit 06633e7

Browse files
committed
solution of top k frequent elements problem based of quickselect (Lomuto, Hoare partitioning; quicksort included)
1 parent e9c319c commit 06633e7

File tree

4 files changed

+116
-15
lines changed

4 files changed

+116
-15
lines changed

data_structures/heap/binary_heap.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ func (h *BinaryHeap) Extract() (int, bool) {
4444
}
4545

4646
rv := h.data[0]
47-
li := len(h.data)-1
47+
li := len(h.data) - 1
4848
h.data[0] = h.data[li]
4949
h.data = h.data[:li]
5050

@@ -61,8 +61,8 @@ func (h *BinaryHeap) Extract() (int, bool) {
6161
*/
6262
func (h *BinaryHeap) siftUp() {
6363
c := len(h.data) - 1
64-
for ;c>0; {
65-
p := (c-1) / 2
64+
for c > 0 {
65+
p := (c - 1) / 2
6666
if h.data[c] < h.data[p] {
6767
break
6868
}
@@ -80,9 +80,9 @@ func (h *BinaryHeap) siftUp() {
8080
1. Compare the new root with its children; if they are in the correct order, stop.
8181
2. If not, swap the element with one of its children and return to the previous step.
8282
(Swap with its smaller child in a min-heap and its larger child in a max-heap.)
83-
*/
83+
*/
8484
func (h *BinaryHeap) siftDown(i int) {
85-
for ;; {
85+
for {
8686
left := 2*i + 1
8787
right := 2*i + 2
8888
largest := i

data_structures/heap/binary_heap_compare_fn.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
package heap
44

55
type BinaryHeapFn struct {
6-
data []int
6+
data []int
77
compareFn func(i, j int) int
88
}
99

@@ -18,7 +18,7 @@ func (h *BinaryHeapFn) Extract() (int, bool) {
1818
}
1919

2020
rv := h.data[0]
21-
li := len(h.data)-1
21+
li := len(h.data) - 1
2222
h.data[0] = h.data[li]
2323
h.data = h.data[:li]
2424

@@ -28,8 +28,8 @@ func (h *BinaryHeapFn) Extract() (int, bool) {
2828

2929
func (h *BinaryHeapFn) siftUp() {
3030
c := len(h.data) - 1
31-
for ;c>0; {
32-
p := (c-1) / 2
31+
for c > 0 {
32+
p := (c - 1) / 2
3333
if h.compareFn(h.data[c], h.data[p]) < 0 {
3434
break
3535
}
@@ -41,7 +41,7 @@ func (h *BinaryHeapFn) siftUp() {
4141
}
4242

4343
func (h *BinaryHeapFn) siftDown(i int) {
44-
for ;; {
44+
for {
4545
left := 2*i + 1
4646
right := 2*i + 2
4747
largest := i

data_structures/heap/binary_heap_compare_fn_test.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,13 @@ package heap
22

33
import "testing"
44

5-
65
var (
7-
compareFnMaxHeap = func (i, j int) int {
8-
return i-j
6+
compareFnMaxHeap = func(i, j int) int {
7+
return i - j
98
}
109

11-
compareFnMinHeap = func (i, j int) int {
12-
return j-i
10+
compareFnMinHeap = func(i, j int) int {
11+
return j - i
1312
}
1413
)
1514

leetcode/top_k_frequent_elements-solution-quickselect.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,105 @@
22

33
// Based on algorithm: https://en.wikipedia.org/wiki/Quickselect
44
package main
5+
6+
func topKFrequent(nums []int, k int) []int {
7+
// O(N)
8+
fs := freqs(nums)
9+
10+
// O(N)
11+
frV := make([]int, len(fs))
12+
elV := make([]int, len(fs))
13+
idx := 0
14+
for eV, fV := range fs {
15+
elV[idx] = eV
16+
frV[idx] = fV
17+
idx++
18+
}
19+
20+
// quickselect - O(N), the worst case will be O(N^2) and we need Hoare partitioning to work with duplicates properly
21+
quickselect(elV, frV, 0, len(fs)-1, len(fs)-k)
22+
return elV[len(elV)-k:]
23+
}
24+
25+
// freqs - returns map[elementValue]frequency(count of elements)
26+
func freqs(nums []int) map[int]int {
27+
res := make(map[int]int)
28+
for i := 0; i < len(nums); i++ {
29+
f, _ := res[nums[i]]
30+
res[nums[i]] = f + 1
31+
}
32+
return res
33+
}
34+
35+
func quickselect(elV, frV []int, left, right, k int) {
36+
for {
37+
if left == right {
38+
return
39+
}
40+
pivotIndex := partitionLomuto(elV, frV, left, right)
41+
if pivotIndex == k {
42+
return
43+
}
44+
if k < pivotIndex {
45+
quickselect(elV, frV, left, pivotIndex-1, k)
46+
return
47+
} else {
48+
quickselect(elV, frV, pivotIndex+1, right, k)
49+
return
50+
}
51+
}
52+
}
53+
54+
func quicksort(elV, frV []int, left, right int) {
55+
if left < right {
56+
pivotIndex := partitionLomuto(elV, frV, left, right)
57+
quicksort(elV, frV, left, pivotIndex-1)
58+
quicksort(elV, frV, pivotIndex+1, right)
59+
}
60+
}
61+
62+
// Hoare partition
63+
func partitionHoare(elV, frV []int, left, right int) int {
64+
pivotIndex := (right + left) / 2
65+
pivot := frV[pivotIndex]
66+
for {
67+
for frV[left] < pivot {
68+
left++
69+
}
70+
for frV[right] > pivot {
71+
right--
72+
}
73+
74+
if left >= right {
75+
return right
76+
}
77+
78+
// swap
79+
swap(frV, left, right)
80+
swap(elV, left, right)
81+
left++
82+
right--
83+
}
84+
}
85+
86+
// Lomuto partition
87+
func partitionLomuto(elV, frV []int, left, right int) int {
88+
pivot := frV[right]
89+
i := left
90+
for j := left; j <= right; j++ {
91+
if frV[j] < pivot {
92+
swap(frV, i, j)
93+
swap(elV, i, j)
94+
i++
95+
}
96+
}
97+
swap(frV, i, right)
98+
swap(elV, i, right)
99+
return i
100+
}
101+
102+
func swap(n []int, i, j int) {
103+
t := n[i]
104+
n[i] = n[j]
105+
n[j] = t
106+
}

0 commit comments

Comments
 (0)