Skip to content

Commit f51a8e5

Browse files
authored
[Arrays & Hashing][Top K Frequent Elements] - Implement solution for Leetcode 347 (#10)
1 parent aa31b3b commit f51a8e5

File tree

7 files changed

+180
-13
lines changed

7 files changed

+180
-13
lines changed

1_arrays_and_hashing/1_contains_duplicate/solution.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ func ContainsDuplicatesBruteForce(nums []int) bool {
1515
return false
1616
}
1717

18-
// Time complexity: O(n log n)
18+
// Time complexity: O(n * log n)
1919
// Space complexity: O(1)
20-
func ContainsDuplicatesSorted(nums []int) bool {
20+
func ContainsDuplicatesSort(nums []int) bool {
2121
sort.Ints(nums)
2222
for i := 1; i < len(nums); i++ {
2323
if nums[i] == nums[i-1] {

1_arrays_and_hashing/1_contains_duplicate/solution_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ func TestContainsDuplicates(t *testing.T) {
1515
{"Brute Force - Empty Slice", ContainsDuplicatesBruteForce, []int{}, false},
1616
{"Brute Force - Single Element", ContainsDuplicatesBruteForce, []int{42}, false},
1717

18-
// ContainsDuplicatesSorted
19-
{"Sorted - Duplicates", ContainsDuplicatesSorted, []int{1, 2, 3, 3}, true},
20-
{"Sorted - No Duplicates", ContainsDuplicatesSorted, []int{1, 2, 3, 4}, false},
21-
{"Sorted - Empty Slice", ContainsDuplicatesSorted, []int{}, false},
22-
{"Sorted - Single Element", ContainsDuplicatesSorted, []int{42}, false},
18+
// ContainsDuplicatesSort
19+
{"Sort - Duplicates", ContainsDuplicatesSort, []int{1, 2, 3, 3}, true},
20+
{"Sort - No Duplicates", ContainsDuplicatesSort, []int{1, 2, 3, 4}, false},
21+
{"Sort - Empty Slice", ContainsDuplicatesSort, []int{}, false},
22+
{"Sort - Single Element", ContainsDuplicatesSort, []int{42}, false},
2323

2424
// ContainsDuplicatesMapBool
2525
{"MapBool - Duplicates", ContainsDuplicatesMapBool, []int{1, 2, 3, 3}, true},

1_arrays_and_hashing/2_valid_anagram/solution.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import (
55
"strings"
66
)
77

8-
// Time complexity: O(n log n)
8+
// Time complexity: O(n * log n)
99
// Space complexity : O(n)
1010
func IsAnagramSort(s, t string) bool {
1111
if len(s) != len(t) {

1_arrays_and_hashing/3_two_sum/solution.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ type Pair struct {
2020
idx int
2121
}
2222

23-
// Time complexity: O(n log n)
23+
// Time complexity: O(n * log n)
2424
// Space complexity: O(n)
2525
func TwoSumSort(nums []int, target int) []int {
2626
pairs := make([]Pair, len(nums))

1_arrays_and_hashing/3_two_sum/solution_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ func TestTwoSum(t *testing.T) {
1616
// TwoSumBruteForce
1717
{"Brute Force - Valid Pair", TwoSumBruteForce, []int{2, 7, 11, 15}, 9, []int{0, 1}},
1818
{"Brute Force - No Pair", TwoSumBruteForce, []int{1, 2, 3}, 7, nil},
19-
{"Brute - Empty", TwoSumBruteForce, []int{}, 0, nil},
20-
{"Brute - Single Element", TwoSumBruteForce, []int{5}, 5, nil},
21-
{"Brute - Negative Numbers", TwoSumBruteForce, []int{-3, 4, 3, 90}, 0, []int{0, 2}},
22-
{"Brute - Duplicates", TwoSumBruteForce, []int{3, 3}, 6, []int{0, 1}},
19+
{"Brute Force - Empty", TwoSumBruteForce, []int{}, 0, nil},
20+
{"Brute Force - Single Element", TwoSumBruteForce, []int{5}, 5, nil},
21+
{"Brute Force - Negative Numbers", TwoSumBruteForce, []int{-3, 4, 3, 90}, 0, []int{0, 2}},
22+
{"Brute Force - Duplicates", TwoSumBruteForce, []int{3, 3}, 6, []int{0, 1}},
2323

2424
// Sorting (Two Pointer)
2525
{"Sort - Valid Pair", TwoSumSort, []int{2, 7, 11, 15}, 9, []int{0, 1}},
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package topkfrequentelements
2+
3+
import (
4+
"container/heap"
5+
"sort"
6+
)
7+
8+
// Time complexity: O(n * log n)
9+
// Space complexity: O(n)
10+
func TopKFrequentSort(nums []int, k int) []int {
11+
freqMap := make(map[int]int)
12+
for _, num := range nums {
13+
freqMap[num]++
14+
}
15+
16+
type pair struct {
17+
num int
18+
freq int
19+
}
20+
21+
pairs := make([]pair, 0, len(freqMap))
22+
for num, freq := range freqMap {
23+
pairs = append(pairs, pair{num, freq})
24+
}
25+
26+
sort.Slice(pairs, func(i, j int) bool {
27+
return pairs[i].freq > pairs[j].freq
28+
})
29+
30+
result := make([]int, 0, k)
31+
for i := 0; i < k; i++ {
32+
result = append(result, pairs[i].num)
33+
}
34+
return result
35+
}
36+
37+
type freqPair struct {
38+
num int
39+
freq int
40+
}
41+
42+
type minHeap []freqPair
43+
44+
func (h minHeap) Len() int { return len(h) }
45+
func (h minHeap) Less(i, j int) bool { return h[i].freq < h[j].freq }
46+
func (h minHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
47+
func (h *minHeap) Push(x interface{}) { *h = append(*h, x.(freqPair)) }
48+
func (h *minHeap) Pop() interface{} {
49+
old := *h
50+
n := len(old)
51+
val := old[n-1]
52+
*h = old[:n-1]
53+
return val
54+
}
55+
56+
// Time complexity: O(n * log k), where n is the number of elements in nums and k is the number of top frequent elements to return.
57+
// Space complexity: O(n)
58+
func TopKFrequentHeap(nums []int, k int) []int {
59+
freqMap := make(map[int]int)
60+
for _, num := range nums {
61+
freqMap[num]++
62+
}
63+
64+
h := &minHeap{}
65+
heap.Init(h)
66+
67+
for num, freq := range freqMap {
68+
heap.Push(h, freqPair{num, freq})
69+
if h.Len() > k {
70+
heap.Pop(h)
71+
}
72+
}
73+
74+
result := make([]int, 0, k)
75+
for h.Len() > 0 {
76+
result = append(result, heap.Pop(h).(freqPair).num)
77+
}
78+
return result
79+
}
80+
81+
// Time complexity: O(n)
82+
// Space complexity: O(n)
83+
func TopKFrequentBucket(nums []int, k int) []int {
84+
freqMap := make(map[int]int)
85+
for _, num := range nums {
86+
freqMap[num]++
87+
}
88+
89+
// max possible frequency is len(nums)
90+
buckets := make([][]int, len(nums)+1)
91+
for num, freq := range freqMap {
92+
buckets[freq] = append(buckets[freq], num)
93+
}
94+
95+
result := []int{}
96+
for i := len(buckets) - 1; i >= 0 && len(result) < k; i-- {
97+
result = append(result, buckets[i]...)
98+
}
99+
return result[:k]
100+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package topkfrequentelements
2+
3+
import (
4+
"reflect"
5+
"sort"
6+
"testing"
7+
)
8+
9+
func sortUnorderedResult(result []int) []int {
10+
sort.Ints(result)
11+
return result
12+
}
13+
14+
func containsAll(target, actual []int) bool {
15+
set := make(map[int]bool)
16+
for _, v := range target {
17+
set[v] = true
18+
}
19+
for _, v := range actual {
20+
if !set[v] {
21+
return false
22+
}
23+
}
24+
return true
25+
}
26+
27+
func TestTopKFrequent(t *testing.T) {
28+
cases := []struct {
29+
name string
30+
fn func([]int, int) []int
31+
nums []int
32+
k int
33+
expected []int
34+
anyOfThese bool // if true, result can contain any subset of expected
35+
}{
36+
// TopKFrequentSort
37+
{"Sort - Basic", TopKFrequentSort, []int{1, 1, 1, 2, 2, 3}, 2, []int{1, 2}, false},
38+
{"Sort - Single", TopKFrequentSort, []int{1}, 1, []int{1}, false},
39+
{"Sort - All Same Freq", TopKFrequentSort, []int{4, 4, 6, 6, 7, 7}, 2, []int{4, 6, 7}, true},
40+
41+
// TopKFrequentHeap
42+
{"Heap - Basic", TopKFrequentHeap, []int{1, 1, 1, 2, 2, 3}, 2, []int{1, 2}, false},
43+
{"Heap - Single", TopKFrequentHeap, []int{1}, 1, []int{1}, false},
44+
{"Heap - All Same Freq", TopKFrequentHeap, []int{4, 4, 6, 6, 7, 7}, 2, []int{4, 6, 7}, true},
45+
46+
// TopKFrequentBucket
47+
{"Bucket - Basic", TopKFrequentBucket, []int{1, 1, 1, 2, 2, 3}, 2, []int{1, 2}, false},
48+
{"Bucket - Single", TopKFrequentBucket, []int{1}, 1, []int{1}, false},
49+
{"Bucket - All Same Freq", TopKFrequentBucket, []int{4, 4, 6, 6, 7, 7}, 2, []int{4, 6, 7}, true},
50+
}
51+
52+
for _, c := range cases {
53+
t.Run(c.name, func(t *testing.T) {
54+
got := c.fn(c.nums, c.k)
55+
sortedGot := sortUnorderedResult(got)
56+
if c.anyOfThese {
57+
if len(got) != c.k || !containsAll(c.expected, got) {
58+
t.Errorf("%s failed: expected any %d of %v, got %v", c.name, c.k, c.expected, sortedGot)
59+
}
60+
} else {
61+
if !reflect.DeepEqual(sortedGot, sortUnorderedResult(c.expected)) {
62+
t.Errorf("%s failed: expected %v, got %v", c.name, c.expected, sortedGot)
63+
}
64+
}
65+
})
66+
}
67+
}

0 commit comments

Comments
 (0)