u
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package top100liked
|
||||
|
||||
import (
|
||||
"container/heap"
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
@@ -136,6 +137,53 @@ func topKFrequentBucket(nums []int, k int) []int {
|
||||
return res
|
||||
}
|
||||
|
||||
// 解法三:container/heap 维护大小 k 的最小堆 — O(n log k) / O(n)
|
||||
// 思路同解法一,但用标准库 container/heap 提供堆算法,自己只实现 heap.Interface。
|
||||
//
|
||||
// container/heap 两层 API(最容易踩坑的地方):
|
||||
// - 你实现的 Push/Pop 只是"回调":Push 追加到切片末尾,Pop 删除末尾元素
|
||||
// - 真正对外的是 heap.Push / heap.Pop:它们调你的 Push/Pop 并负责 siftUp/siftDown
|
||||
// - Less 用 < 是小顶堆(堆顶最小),用 > 是大顶堆
|
||||
// - Push/Pop 必须用指针接收者(要改 slice header);Len/Less/Swap 用值接收者即可
|
||||
//
|
||||
// 要点:
|
||||
// - entryHeap []entry 实现 heap.Interface(Len/Less/Swap + Push/Pop)
|
||||
// - Less 按 cnt 升序,堆顶是堆内频率最小者
|
||||
// - 遍历 m:heap.Push(h, entry{v,c});h.Len() > k 时 heap.Pop(h) 淘汰最小
|
||||
// - 收集堆中剩余 k 个元素返回
|
||||
type entryHeap []entry
|
||||
|
||||
func (h entryHeap) Len() int { return len(h) }
|
||||
func (h entryHeap) Less(i, j int) bool { return h[i].cnt < h[j].cnt } // < 小顶堆
|
||||
func (h entryHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
|
||||
|
||||
// Push/Pop 是给 heap 包调用的回调,只负责在切片末尾增删,不要加堆序逻辑
|
||||
func (h *entryHeap) Push(x any) { *h = append(*h, x.(entry)) }
|
||||
func (h *entryHeap) Pop() any {
|
||||
old := *h
|
||||
n := len(old)
|
||||
x := old[n-1]
|
||||
*h = old[:n-1]
|
||||
return x
|
||||
}
|
||||
|
||||
func topKFrequentContainerHeap(nums []int, k int) []int {
|
||||
m := countFreq(nums)
|
||||
h := &entryHeap{}
|
||||
heap.Init(h)
|
||||
for v, c := range m {
|
||||
heap.Push(h, entry{v, c})
|
||||
if h.Len() > k {
|
||||
heap.Pop(h)
|
||||
}
|
||||
}
|
||||
res := make([]int, 0, k)
|
||||
for h.Len() > 0 {
|
||||
res = append(res, heap.Pop(h).(entry).val)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// equalIgnoreOrder 忽略顺序比较两个 []int。
|
||||
func equalIgnoreOrder(a, b []int) bool {
|
||||
if len(a) != len(b) {
|
||||
@@ -167,6 +215,7 @@ func TestTopKFrequent(t *testing.T) {
|
||||
}{
|
||||
{"heap", topKFrequentHeap},
|
||||
{"bucket", topKFrequentBucket},
|
||||
{"container-heap", topKFrequentContainerHeap},
|
||||
}
|
||||
for _, s := range solvers {
|
||||
t.Run(s.name, func(t *testing.T) {
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
package top100liked
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// https://leetcode.cn/problems/find-median-from-data-stream/?envType=study-plan-v2&envId=top-100-liked
|
||||
|
||||
type heap[T any] struct {
|
||||
data []T
|
||||
compareFunc func(T, T) bool
|
||||
}
|
||||
|
||||
func NewHeap[T any](compareFunc func(T, T) bool) heap[T] {
|
||||
return heap[T]{
|
||||
compareFunc: compareFunc,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *heap[T]) Up(idx int) {
|
||||
for (idx-1)/2 >= 0 {
|
||||
cur := h.data[idx]
|
||||
p := h.data[(idx-1)/2]
|
||||
if h.compareFunc(cur, p) {
|
||||
h.data[idx], h.data[(idx-1)/2] = h.data[(idx-1)/2], h.data[idx]
|
||||
idx = (idx - 1) / 2
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (h *heap[T]) Down(idx int) {
|
||||
for {
|
||||
l, r := 2*idx+1, 2*idx+2
|
||||
m := idx
|
||||
if l < len(h.data) && h.compareFunc(h.data[l], h.data[m]) {
|
||||
m = l
|
||||
}
|
||||
if r < len(h.data) && h.compareFunc(h.data[r], h.data[m]) {
|
||||
m = r
|
||||
}
|
||||
if m == idx {
|
||||
break
|
||||
}
|
||||
h.data[idx], h.data[m] = h.data[m], h.data[idx]
|
||||
idx = m
|
||||
}
|
||||
}
|
||||
|
||||
func (h *heap[T]) Push(val T) {
|
||||
h.data = append(h.data, val)
|
||||
h.Up(len(h.data) - 1)
|
||||
}
|
||||
|
||||
func (h *heap[T]) Pop() T {
|
||||
if len(h.data) <= 0 {
|
||||
var zero T
|
||||
return zero
|
||||
}
|
||||
old := h.data[0]
|
||||
h.data[0] = h.data[len(h.data)-1]
|
||||
h.data = h.data[:len(h.data)-1]
|
||||
h.Down(0)
|
||||
return old
|
||||
}
|
||||
|
||||
func (h *heap[T]) Top() T {
|
||||
if len(h.data) > 0 {
|
||||
return h.data[0]
|
||||
}
|
||||
var zero T
|
||||
return zero
|
||||
}
|
||||
|
||||
func (h *heap[T]) Len() int {
|
||||
return len(h.data)
|
||||
}
|
||||
|
||||
type MedianFinder struct {
|
||||
left heap[int] // 小
|
||||
right heap[int] // 大
|
||||
}
|
||||
|
||||
func Constructor() MedianFinder {
|
||||
return MedianFinder{
|
||||
left: NewHeap(func(i1, i2 int) bool { return i1 > i2 }),
|
||||
right: NewHeap(func(i1, i2 int) bool { return i1 < i2 }),
|
||||
}
|
||||
}
|
||||
|
||||
func (this *MedianFinder) AddNum(num int) {
|
||||
if this.left.Len() == 0 {
|
||||
this.left.Push(num)
|
||||
return
|
||||
}
|
||||
if num >= this.left.Top() {
|
||||
this.right.Push(num)
|
||||
} else {
|
||||
this.left.Push(num)
|
||||
}
|
||||
if this.left.Len() > this.right.Len()+1 {
|
||||
v := this.left.Pop()
|
||||
this.right.Push(v)
|
||||
}
|
||||
if this.right.Len() > this.left.Len()+1 {
|
||||
v := this.right.Pop()
|
||||
this.left.Push(v)
|
||||
}
|
||||
}
|
||||
|
||||
func (this *MedianFinder) FindMedian() float64 {
|
||||
switch (this.left.Len() + this.right.Len()) % 2 {
|
||||
case 0:
|
||||
return (float64(this.left.Top()) + float64(this.right.Top())) / 2
|
||||
case 1:
|
||||
if this.left.Len() > this.right.Len() {
|
||||
return float64(this.left.Top())
|
||||
} else {
|
||||
return float64(this.right.Top())
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func Test(t *testing.T) {
|
||||
medianFinder := Constructor()
|
||||
medianFinder.AddNum(1) // arr = [1]
|
||||
medianFinder.AddNum(2) // arr = [1, 2]
|
||||
fmt.Println(medianFinder.FindMedian()) // 返回 1.5 ((1 + 2) / 2)
|
||||
medianFinder.AddNum(3) // arr[1, 2, 3]
|
||||
fmt.Println(medianFinder.FindMedian()) // return 2.0
|
||||
}
|
||||
Reference in New Issue
Block a user