Skip to content

Commit

Permalink
Merge pull request #12 from vaskoz/skyline
Browse files Browse the repository at this point in the history
skyline problem
  • Loading branch information
vaskoz committed Sep 19, 2018
2 parents 570c632 + b2b260d commit b9add06
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 0 deletions.
1 change: 1 addition & 0 deletions skyline/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Skyline problem
87 changes: 87 additions & 0 deletions skyline/problem.go
Original file line number Diff line number Diff line change
@@ -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
}
37 changes: 37 additions & 0 deletions skyline/problem_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
}
}

0 comments on commit b9add06

Please sign in to comment.