Repository: vaskoz/dailycodingproblem-go (go)

Issue:
Day 33
Good morning! Here's your coding interview problem for today.

This problem was asked by Microsoft.

Compute the running median of a sequence of numbers. That is, given a stream of numbers, print out the median of the list so far on each new element.

Recall that the median of an even-numbered list is the average of the two middle numbers.

For example, given the sequence `[2, 1, 5, 7, 2, 0, 5]`, your algorithm should print out:
`
2
1.5
2
3.5
2
2
2
`

Pull request description:
Day33
Resolves #73.

Patch:
diff --git a/README.md b/README.md
index fb066e2..f215268 100644
--- a/README.md
+++ b/README.md
@@ -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)
diff --git a/day33/problem.go b/day33/problem.go
new file mode 100644
index 0000000..0e07cd6
--- /dev/null
+++ b/day33/problem.go
@@ -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])
+}


Test patch:
diff --git a/day33/problem_test.go b/day33/problem_test.go
new file mode 100644
index 0000000..5b31cf8
--- /dev/null
+++ b/day33/problem_test.go
@@ -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)
+			}
+		}
+	}
+}