Skip to content

Commit

Permalink
Merge pull request #74 from vaskoz/day33
Browse files Browse the repository at this point in the history
Day33
  • Loading branch information
vaskoz committed Sep 24, 2018
2 parents fef4eef + 36f194e commit 31fca5f
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,4 @@ problems from
* [Day 28](https://github.com/vaskoz/dailycodingproblem-go/issues/65)
* [Day 29](https://github.com/vaskoz/dailycodingproblem-go/issues/67)
* [Day 30](https://github.com/vaskoz/dailycodingproblem-go/issues/69)
* [Day 33](https://github.com/vaskoz/dailycodingproblem-go/issues/73)
79 changes: 79 additions & 0 deletions day33/problem.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package day33

import "container/heap"

type MinPQ []int
type MaxPQ []int

type RunningMedian struct {
low *MaxPQ
high *MinPQ
}

func (pq MinPQ) Len() int { return len(pq) }

func (pq MinPQ) Less(i, j int) bool {
return pq[i] < pq[j]
}

func (pq MinPQ) Swap(i, j int) {
pq[i], pq[j] = pq[j], pq[i]
}

func (pq *MinPQ) Push(x interface{}) {
item := x.(int)
*pq = append(*pq, item)
}

func (pq *MinPQ) Pop() interface{} {
old := *pq
n := len(old)
item := old[n-1]
*pq = old[0 : n-1]
return item
}

func (pq MaxPQ) Len() int { return len(pq) }

func (pq MaxPQ) Less(i, j int) bool {
return pq[i] > pq[j]
}

func (pq MaxPQ) Swap(i, j int) {
pq[i], pq[j] = pq[j], pq[i]
}

func (pq *MaxPQ) Push(x interface{}) {
item := x.(int)
*pq = append(*pq, item)
}

// Pop is not called, but needed to meet the interface.
func (pq *MaxPQ) Pop() interface{} {
old := *pq
n := len(old)
item := old[n-1]
*pq = old[0 : n-1]
return item
}

// NewRunningMedian returns a new instance of a RunningMedian processor.
func NewRunningMedian() *RunningMedian {
return &RunningMedian{
&MaxPQ{},
&MinPQ{},
}
}

// Median returns the running median after adding this value.
func (rm *RunningMedian) Median(val int) float64 {
heap.Push(rm.high, val)
if rm.high.Len() > rm.low.Len()+1 {
v := heap.Pop(rm.high)
heap.Push(rm.low, v)
}
if rm.high.Len() == rm.low.Len() {
return float64((*rm.high)[0]+(*rm.low)[0]) / 2
}
return float64((*rm.high)[0])
}
70 changes: 70 additions & 0 deletions day33/problem_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package day33

import (
"container/heap"
"testing"
)

var testcases = []struct {
input []int
expected []float64
}{
{[]int{2, 1, 5, 7, 2, 0, 5}, []float64{2, 1.5, 2, 3.5, 2, 2, 2}},
}

func TestRunningMedian(t *testing.T) {
t.Parallel()
delta := 0.01
for _, tc := range testcases {
rm := NewRunningMedian()
var mismatch bool
results := make([]int, 0, len(tc.input))
for i, v := range tc.input {
result := rm.Median(v)
if lower, upper := result-delta, result+delta; tc.expected[i] > upper || tc.expected[i] < lower {
mismatch = true
results = append(results, v)
}
}
if mismatch {
t.Errorf("Expected %v got %v", tc.expected, results)
}
}
}

func TestHeaps(t *testing.T) {
t.Parallel()
data := []int{1, 2, 3}
min := &MinPQ{}
max := &MaxPQ{}
for _, v := range data {
heap.Push(min, v)
heap.Push(max, v)
}
if min.Len() != max.Len() {
t.Errorf("Lengths should match %v and %v", min.Len(), max.Len())
}
for i := range data {
x := heap.Pop(min)
if x != data[i] {
t.Errorf("Expected %v but got %v", data[i], x)
}
}
for i := range data {
x := heap.Pop(max)
if x != data[len(data)-i-1] {
t.Errorf("Expected %v but got %v", data[len(data)-i-1], x)
}
}
}

func BenchmarkRunningMedian(b *testing.B) {
for i := 0; i < b.N; i++ {
for _, tc := range testcases {
rm := NewRunningMedian()
for _, v := range tc.input {
rm.Median(v)
}
}
}
}

0 comments on commit 31fca5f

Please sign in to comment.