-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #12 from vaskoz/skyline
skyline problem
- Loading branch information
Showing
3 changed files
with
125 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
# Skyline problem |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} | ||
} |