-
Notifications
You must be signed in to change notification settings - Fork 2
/
skyline.groovy
113 lines (100 loc) · 3.02 KB
/
skyline.groovy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
/**
* The Skyline Problem
* http://online-judge.uva.es/p/v1/105.html
*
* Sweep Line Algorithm
* http://www.algorithmist.com/index.php/UVa_105
*/
import static Operation.*
/*
* To implement the algorithm we need two data structures:
* - min-heap to store insert-remove events, and
* - balanced binary search tree to store building heights
*/
class MaxIntegerList {
private List<Integer> list // TODO: replace by BST
MaxIntegerList(int initialCapacity) {
list = new ArrayList<Integer>(initialCapacity)
}
boolean add(Integer i) {
list << i
}
Integer max() {
list.max()
}
def remove(Integer i) {
int index = list.indexOf(i)
index < 0 ? null : list.remove(index)
}
}
enum Operation {
INSERT, REMOVE
}
class Event {
Integer coordinate
Integer height
Operation operation
}
class MinEventHeap {
private PriorityQueue<Event> queue
MinEventHeap(int initialCapacity) {
queue = new PriorityQueue<Event>(initialCapacity, new EventComporator())
}
boolean insert(Event i) {
queue.add i
}
Event extractMin() {
queue.poll()
}
int size() {
queue.size()
}
static class EventComporator implements Comparator<Event> {
int compare(Event p, Event q) {
if (p.coordinate != q.coordinate) return p.coordinate.compareTo(q.coordinate)
if (p.operation == INSERT && q.operation == INSERT) return q.height.compareTo(p.height)
if (p.operation == REMOVE && q.operation == REMOVE) return p.height.compareTo(q.height)
p.operation == INSERT ? -1 : 1
}
boolean equals(Object o) {
return o instanceof EventComporator
}
}
}
/*
* Main algorithm
*/
def skyline(buildings) {
def eventHeap = buildEventHeap(buildings)
processEvents(eventHeap, new MaxIntegerList(buildings.size()))
}
def buildEventHeap(buildings) {
def result = new MinEventHeap(2 * buildings.size())
buildings.each { // it = [x1, height, x2]
result.insert(new Event(coordinate: it[0], height: it[1], operation: INSERT))
result.insert(new Event(coordinate: it[2], height: it[1], operation: REMOVE))
}
result
}
def processEvents(eventHeap, heights) {
def result = []
int x = Integer.MIN_VALUE, height = 0
while ((event = eventHeap.extractMin()) != null) {
// 1. Process event
event.operation == INSERT ? heights.add(event.height) : heights.remove(event.height)
// 2. Get maximum height
def h = heights.max() ?: 0
// 3. Add new output on height change
if (height != h) {
x = event.coordinate
height = h
result << x << height
}
}
result
}
assert [2,4,8,0] == skyline([[2,4,5],[4,4,8]])
assert [2,4,8,0] == skyline([[2,4,5],[5,4,8]])
assert [2,4,6,0] == skyline([[2,4,6],[2,2,6]])
assert [1,11,3,13,9,0,12,7,16,3,19,18,22,3,23,13,29,0] ==
skyline([[1,11,5],[2,6,7],[3,13,9],[12,7,16],[14,3,25],[19,18,22],[23,13,29],[24,4,28]])