From b2b260de43cad7fbd91783e2699ab5a225998dfa Mon Sep 17 00:00:00 2001 From: Vasko Zdravevski Date: Wed, 19 Sep 2018 11:36:32 -0600 Subject: [PATCH] skyline problem --- skyline/README.md | 1 + skyline/problem.go | 87 +++++++++++++++++++++++++++++++++++++++++ skyline/problem_test.go | 37 ++++++++++++++++++ 3 files changed, 125 insertions(+) create mode 100644 skyline/README.md create mode 100644 skyline/problem.go create mode 100644 skyline/problem_test.go diff --git a/skyline/README.md b/skyline/README.md new file mode 100644 index 0000000..11c0557 --- /dev/null +++ b/skyline/README.md @@ -0,0 +1 @@ +# Skyline problem diff --git a/skyline/problem.go b/skyline/problem.go new file mode 100644 index 0000000..9b4319e --- /dev/null +++ b/skyline/problem.go @@ -0,0 +1,87 @@ +package skyline + +import ( + "container/heap" + "sort" +) + +// Point represents a 0-dimensional geometric point. +type Point struct { + X, Y int +} + +// Building represents a 2-dimensional representation of a building in a skyline. +type Building struct { + LeftX, RightX, Height int +} + +type edge struct { + X, Height int + Up bool +} + +// HeightHeap is a max-heap of building heights. +type HeightHeap []int + +func (h HeightHeap) Len() int { return len(h) } +func (h HeightHeap) Less(i, j int) bool { return h[i] > h[j] } +func (h HeightHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } + +// Push adds a new height to the heap. +func (h *HeightHeap) Push(x interface{}) { + *h = append(*h, x.(int)) +} + +// Pop removes and returns the max height from the heap. +func (h *HeightHeap) Pop() interface{} { + old := *h + n := len(old) + x := old[n-1] + *h = old[0 : n-1] + return x +} + +// Skyline returns all the points describing a skyline silhouette. +// Runs in O(N log N) time with some extra space O(N) +func Skyline(buildings []Building) []Point { + edges := make([]edge, 0, 2*len(buildings)) + for _, building := range buildings { + edges = append(edges, edge{building.LeftX, building.Height, true}) + edges = append(edges, edge{building.RightX, building.Height, false}) + } + sort.Slice(edges, func(i, j int) bool { + return edges[i].X < edges[j].X + }) + var points []Point + h := &HeightHeap{} + heap.Init(h) + for _, edge := range edges { + if edge.Up { + if h.Len() == 0 { + points = append(points, Point{edge.X, 0}) + points = append(points, Point{edge.X, edge.Height}) + } else if edge.Height > (*h)[0] { + points = append(points, Point{edge.X, (*h)[0]}) + points = append(points, Point{edge.X, edge.Height}) + } + heap.Push(h, edge.Height) + } else if !edge.Up { + var removeIndex int + for i, v := range *h { + if v == edge.Height { + removeIndex = i + break + } + } + heap.Remove(h, removeIndex) + if h.Len() == 0 { + points = append(points, Point{edge.X, edge.Height}) + points = append(points, Point{edge.X, 0}) + } else if edge.Height > (*h)[0] { + points = append(points, Point{edge.X, edge.Height}) + points = append(points, Point{edge.X, (*h)[0]}) + } + } + } + return points +} diff --git a/skyline/problem_test.go b/skyline/problem_test.go new file mode 100644 index 0000000..c4af310 --- /dev/null +++ b/skyline/problem_test.go @@ -0,0 +1,37 @@ +package skyline + +import ( + "reflect" + "testing" +) + +var testcases = []struct { + buildings []Building + expected []Point +}{ + {[]Building{ + {5, 7, 10}, + {8, 20, 5}, + {9, 12, 7}, + {10, 15, 12}, + {18, 25, 6}, + }, + []Point{{5, 0}, {5, 10}, {7, 10}, {7, 0}, {8, 0}, {8, 5}, {9, 5}, {9, 7}, {10, 7}, {10, 12}, + {15, 12}, {15, 5}, {18, 5}, {18, 6}, {25, 6}, {25, 0}}}, +} + +func TestSkyline(t *testing.T) { + for _, tc := range testcases { + if skyline := Skyline(tc.buildings); !reflect.DeepEqual(skyline, tc.expected) { + t.Errorf("Expected %v got %v", tc.expected, skyline) + } + } +} + +func BenchmarkSkyline(b *testing.B) { + for i := 0; i < b.N; i++ { + for _, tc := range testcases { + Skyline(tc.buildings) + } + } +}