-
Notifications
You must be signed in to change notification settings - Fork 0
/
skewer.go
255 lines (204 loc) · 5.51 KB
/
skewer.go
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
// Package skewer - a mergable priority queue
//
// Skew heaps implement a priority queue (min heap) using a binary heap which
// is continually rebalanced with each Put and Take operation. Skew heaps have
// an ammortized performance slighter better than O(log n).
//
// The key feature of a skew heap is that it may be quickly and trivially
// merged with another skew heap. All heap operations are defined in terms of
// the merge operation.
//
// Mutable operations on the skew heap are atomic.
//
// For more details, see https://en.wikipedia.org/wiki/Skew_heap
package skewer
import "errors"
import "fmt"
import "sort"
import "sync"
// SkewItem requires any queueable value to be wrapped in an interface which
// can provide a relative priority for the value as an integer. A lower value
// indicates a higher priority in the queue.
type SkewItem interface {
Priority() int
}
type skewNode struct {
left, right *skewNode
value SkewItem
}
func (node skewNode) priority() int {
return node.value.Priority()
}
// SkewHeap is the base interface type
type SkewHeap struct {
// The number of items in the queue
size int
mutex *sync.Mutex
root *skewNode
}
// Size returns the number of items in the queue.
func (heap SkewHeap) Size() int { return heap.size }
// Sort interface
type byPriority []*skewNode
func (a byPriority) Len() int { return len(a) }
func (a byPriority) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a byPriority) Less(i, j int) bool { return a[i].priority() < a[j].priority() }
// New initializes and returns a new *SkewHeap.
func New() *SkewHeap {
heap := &SkewHeap{
size: 0,
mutex: &sync.Mutex{},
root: nil,
}
return heap
}
// Voluntarily locks the data structure while modifying it.
func (heap *SkewHeap) lock() { heap.mutex.Lock() }
func (heap *SkewHeap) unlock() { heap.mutex.Unlock() }
// Indents explain()
func indent(depth int) {
for i := 0; i < depth; i++ {
fmt.Print(" ")
}
}
// Debugging routine that emits a description of the node and its internal
// structure to stdout.
func (node skewNode) explain(depth int) {
indent(depth)
fmt.Printf("Node<value:%v, priority:%d>\n", node.value, node.priority())
if node.left != nil {
indent(depth)
fmt.Printf("-Left:\n")
node.left.explain(depth + 1)
}
if node.right != nil {
indent(depth)
fmt.Printf("-Right:\n")
node.right.explain(depth + 1)
}
}
// Explain emits a description of the skew heap and its internal structure to
// stdout.
func (heap SkewHeap) Explain() {
fmt.Printf("Heap<Size:%d>\n", heap.Size())
fmt.Printf("-Root:\n")
if heap.Size() > 0 {
heap.root.explain(1)
}
fmt.Printf("\n")
}
// Merges two nodes destructively
func (node *skewNode) merge(other *skewNode) *skewNode {
if node == nil {
return other
}
if other == nil {
return node
}
// Cut the right subtree from each path and store the remaining left subtrees
// in nodes.
todo := [](*skewNode){node, other}
nodes := [](*skewNode){}
for len(todo) > 0 {
last := todo[0]
todo = todo[1:]
if last.right != nil {
todo = append(todo, last.right)
last.right = nil
}
nodes = append(nodes, last)
}
// Sort the cut paths
sort.Sort(byPriority(nodes))
// Recombine subtrees
var last *skewNode
for len(nodes) > 1 {
last, nodes = nodes[len(nodes)-1], nodes[:len(nodes)-1]
prev := nodes[len(nodes)-1]
// Set penultimate node's right child to its left (and only) subtree
prev.right = prev.left
// Set its left child to the ultimate node
prev.left = last
}
return nodes[0]
}
// Recursively copies a node and its children
func (node *skewNode) copyNode() *skewNode {
if node == nil {
return nil
}
newNode := &skewNode{
value: node.value,
left: node.left.copyNode(),
right: node.right.copyNode(),
}
return newNode
}
// Merge non-destructively combines two heaps into a new heap. Note that Merge
// recursively copies the structure of each input heap.
func (heap SkewHeap) Merge(other SkewHeap) *SkewHeap {
ready := make(chan bool, 2)
var rootA, rootB *skewNode
var sizeA, sizeB int
// Because each heap may be used by other go routines, locking their mutexes
// and copying their contents is done in another routine, and this thread
// blocks on receiving a signal from the locking thread. This helps to avoid
// unnecessary blocking by attempting to lock two mutexes serially.
go func() {
heap.lock()
sizeA = heap.Size()
rootA = heap.root.copyNode()
heap.unlock()
ready <- true
}()
go func() {
other.lock()
sizeB = other.Size()
rootB = other.root.copyNode()
other.unlock()
ready <- true
}()
// Wait on copies to be made
<-ready
<-ready
newHeap := New()
newHeap.size += sizeA + sizeB
newHeap.root = rootA.merge(rootB)
return newHeap
}
// Put inserts a value into the heap.
func (heap *SkewHeap) Put(value SkewItem) {
newNode := &skewNode{
left: nil,
right: nil,
value: value,
}
heap.lock()
if heap.Size() == 0 {
heap.root = newNode
} else {
heap.root = heap.root.merge(newNode)
}
heap.size++
heap.unlock()
}
// Take removes and returns the value with the highest priority from the heap.
func (heap *SkewHeap) Take() (SkewItem, error) {
heap.lock()
if heap.Size() > 0 {
value := heap.root.value
heap.root = heap.root.left.merge(heap.root.right)
heap.size--
heap.unlock()
return value, nil
}
heap.unlock()
return nil, errors.New("empty")
}
// Top returns the value highest priority from the heap without removing it.
func (heap *SkewHeap) Top() (SkewItem, error) {
if heap.Size() > 0 {
return heap.root.value, nil
}
return nil, errors.New("empty")
}