package main
import (
"container/heap"
"fmt"
)
// IntHeap is a min-heap of ints.
type IntHeap []int
func (h IntHeap) Len() int { return len(h) }
func (h IntHeap) Less(i, j int) bool { return h[i] < h[j] }
func (h IntHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *IntHeap) Push(x interface{}) {
*h = append(*h, x.(int))
}
func (h *IntHeap) Pop() interface{} {
n := len(*h)
x := (*h)[n-1]
*h = (*h)[:n-1]
return x
}
// MaxHeap is a max-heap of ints by inverting Less
type MaxHeap []int
func (h MaxHeap) Len() int { return len(h) }
func (h MaxHeap) Less(i, j int) bool { return h[i] > h[j] }
func (h MaxHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *MaxHeap) Push(x interface{}) {
*h = append(*h, x.(int))
}
func (h *MaxHeap) Pop() interface{} {
n := len(*h)
x := (*h)[n-1]
*h = (*h)[:n-1]
return x
}
// MedianFinder holds two heaps
// maxHeap for smaller half, minHeap for larger half
// sizes differ by at most 1
type MedianFinder struct {
maxHeap *MaxHeap
minHeap *IntHeap
}
func Constructor() MedianFinder {
maxH := &MaxHeap{}
minH := &IntHeap{}
heap.Init(maxH)
heap.Init(minH)
return MedianFinder{maxHeap: maxH, minHeap: minH}
}
func (mf *MedianFinder) AddNum(num int) {
if mf.maxHeap.Len() == 0 || num <= (*mf.maxHeap)[0] {
heap.Push(mf.maxHeap, num) // add to smaller half
} else {
heap.Push(mf.minHeap, num) // add to larger half
}
// Balance sizes
if mf.maxHeap.Len() > mf.minHeap.Len()+1 {
val := heap.Pop(mf.maxHeap).(int) // move top from maxHeap to minHeap
heap.Push(mf.minHeap, val)
} else if mf.minHeap.Len() > mf.maxHeap.Len() {
val := heap.Pop(mf.minHeap).(int) // move top from minHeap to maxHeap
heap.Push(mf.maxHeap, val)
}
}
func (mf *MedianFinder) FindMedian() float64 {
if mf.maxHeap.Len() == 0 {
return 0.0
}
if mf.maxHeap.Len() > mf.minHeap.Len() {
return float64((*mf.maxHeap)[0])
} else {
return (float64((*mf.maxHeap)[0]) + float64((*mf.minHeap)[0])) / 2.0
}
}
func main() {
mf := Constructor()
nums := []int{5, 3, 8, 9, 1, 6}
for _, num := range nums {
mf.AddNum(num)
median := mf.FindMedian()
fmt.Printf("Added %d, current median: %.1f\n", num, median)
}
}
if mf.maxHeap.Len() == 0 || num <= (*mf.maxHeap)[0] {
decide which heap to add new number to based on comparison with maxHeap top
heap.Push(mf.maxHeap, num)
add number to smaller half maxHeap
heap.Push(mf.minHeap, num)
add number to larger half minHeap
if mf.maxHeap.Len() > mf.minHeap.Len()+1 {
check if smaller half is too big and rebalance
val := heap.Pop(mf.maxHeap).(int)
remove top from smaller half to move to larger half
heap.Push(mf.minHeap, val)
add removed top to larger half
else if mf.minHeap.Len() > mf.maxHeap.Len() {
check if larger half is too big and rebalance
val := heap.Pop(mf.minHeap).(int)
remove top from larger half to move to smaller half
heap.Push(mf.maxHeap, val)
add removed top to smaller half
if mf.maxHeap.Len() > mf.minHeap.Len() {
if smaller half bigger, median is top of maxHeap
return float64((*mf.maxHeap)[0])
return median as top of smaller half
return (float64((*mf.maxHeap)[0]) + float64((*mf.minHeap)[0])) / 2.0
if equal size, median is average of tops
Added 5, current median: 5.0
Added 3, current median: 4.0
Added 8, current median: 5.0
Added 9, current median: 6.5
Added 1, current median: 5.0
Added 6, current median: 5.5