algorithms 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +139 -2
- data/Manifest +31 -8
- data/README +90 -0
- data/Rakefile +22 -9
- data/algorithms.gemspec +28 -101
- data/benchmark.rb +29 -27
- data/benchmarks/rbench.rb +16 -0
- data/benchmarks/rbench/column.rb +26 -0
- data/benchmarks/rbench/group.rb +43 -0
- data/benchmarks/rbench/report.rb +53 -0
- data/benchmarks/rbench/runner.rb +109 -0
- data/benchmarks/rbench/summary.rb +51 -0
- data/benchmarks/sorts.rb +33 -0
- data/ext/containers/bst/bst.c +205 -0
- data/ext/containers/{priority_queue → bst}/extconf.rb +1 -1
- data/ext/containers/deque/deque.c +233 -0
- data/ext/containers/deque/extconf.rb +4 -0
- data/ext/containers/tree_map/extconf.rb +1 -1
- data/ext/containers/tree_map/rbtree.c +73 -25
- data/lib/algorithms.rb +65 -6
- data/lib/algorithms/search.rb +84 -0
- data/lib/algorithms/sort.rb +238 -0
- data/lib/containers/deque.rb +176 -0
- data/lib/containers/heap.rb +451 -111
- data/lib/containers/kd_tree.rb +87 -0
- data/lib/containers/priority_queue.rb +107 -508
- data/lib/containers/queue.rb +62 -23
- data/lib/containers/rb_tree_map.rb +398 -0
- data/lib/containers/splay_tree_map.rb +274 -0
- data/lib/containers/stack.rb +59 -21
- data/lib/containers/suffix_array.rb +68 -0
- data/lib/containers/trie.rb +182 -0
- data/lib/graphs/graph.rb +25 -0
- data/spec/bst_spec.rb +31 -0
- data/spec/deque_spec.rb +108 -0
- data/spec/heap_spec.rb +111 -66
- data/spec/kd_tree_spec.rb +89 -0
- data/spec/priority_queue_spec.rb +71 -27
- data/spec/queue_spec.rb +53 -45
- data/spec/rb_tree_map_spec.rb +123 -0
- data/spec/search_spec.rb +28 -0
- data/spec/sort_spec.rb +28 -0
- data/spec/splay_tree_map_spec.rb +102 -0
- data/spec/stack_spec.rb +56 -49
- data/spec/suffix_array_spec.rb +40 -0
- data/spec/trie_spec.rb +59 -0
- metadata +54 -32
- data/README.txt +0 -58
- data/ext/containers/priority_queue/priority_queue.c +0 -948
- data/ext/containers/tree_map/Rakefile +0 -4
- data/lib/containers/hash.rb +0 -0
- data/lib/containers/tree_map.rb +0 -265
- data/spec/priority_queue_test.rb +0 -371
- data/spec/tree_map_spec.rb +0 -99
@@ -0,0 +1,87 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
|
3
|
+
A kd-tree allows searching of points in multi-dimensional space, increasing
|
4
|
+
efficiency for nearest-neighbor searching in particular.
|
5
|
+
|
6
|
+
=end
|
7
|
+
|
8
|
+
class Containers::KDTree
|
9
|
+
Node = Struct.new(:id, :coords, :left, :right)
|
10
|
+
|
11
|
+
def initialize(points)
|
12
|
+
@root = build_tree(points)
|
13
|
+
@nearest = []
|
14
|
+
end
|
15
|
+
|
16
|
+
# Build a kd-tree
|
17
|
+
def build_tree(points, depth=0)
|
18
|
+
return if points.empty?
|
19
|
+
|
20
|
+
axis = depth % 2
|
21
|
+
|
22
|
+
points.sort! { |a, b| a[1][axis] <=> b[1][axis] }
|
23
|
+
median = points.size / 2
|
24
|
+
|
25
|
+
node = Node.new(points[median][0], points[median][1], nil, nil)
|
26
|
+
node.left = build_tree(points[0...median], depth+1)
|
27
|
+
node.right = build_tree(points[median+1..-1], depth+1)
|
28
|
+
node
|
29
|
+
end
|
30
|
+
private :build_tree
|
31
|
+
|
32
|
+
# Euclidian distanced, squared, between a node and target coords
|
33
|
+
def distance2(node, target)
|
34
|
+
return nil if node.nil? or target.nil?
|
35
|
+
c = (node.coords[0] - target[0])
|
36
|
+
d = (node.coords[1] - target[1])
|
37
|
+
c * c + d * d
|
38
|
+
end
|
39
|
+
private :distance2
|
40
|
+
|
41
|
+
# Update array of nearest elements if necessary
|
42
|
+
def check_nearest(nearest, node, target, k_nearest)
|
43
|
+
d = distance2(node, target)
|
44
|
+
if nearest.size < k_nearest || d < nearest.last[0]
|
45
|
+
nearest.pop if nearest.size >= k_nearest
|
46
|
+
nearest << [d, node.id]
|
47
|
+
nearest.sort! { |a, b| a[0] <=> b[0] }
|
48
|
+
end
|
49
|
+
nearest
|
50
|
+
end
|
51
|
+
|
52
|
+
# Find k closest points to given coordinates
|
53
|
+
def find_nearest(target, k_nearest)
|
54
|
+
@nearest = []
|
55
|
+
nearest(@root, target, k_nearest, 0)
|
56
|
+
end
|
57
|
+
|
58
|
+
def nearest(node, target, k_nearest, depth)
|
59
|
+
axis = depth % 2
|
60
|
+
|
61
|
+
if node.left.nil? && node.right.nil? # Leaf node
|
62
|
+
@nearest = check_nearest(@nearest, node, target, k_nearest)
|
63
|
+
return
|
64
|
+
end
|
65
|
+
|
66
|
+
# Go down the nearest split
|
67
|
+
if node.right.nil? || (node.left && target[axis] <= node.coords[axis])
|
68
|
+
nearer = node.left
|
69
|
+
further = node.right
|
70
|
+
else
|
71
|
+
nearer = node.right
|
72
|
+
further = node.left
|
73
|
+
end
|
74
|
+
nearest(nearer, target, k_nearest, depth+1)
|
75
|
+
|
76
|
+
# See if we have to check other side
|
77
|
+
if further
|
78
|
+
if @nearest.size < k_nearest || (target[axis] - node.coords[axis])**2 < @nearest.last[0]
|
79
|
+
nearest(further, target, k_nearest, depth+1)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
@nearest = check_nearest(@nearest, node, target, k_nearest)
|
84
|
+
end
|
85
|
+
private :nearest
|
86
|
+
|
87
|
+
end
|
@@ -1,514 +1,113 @@
|
|
1
|
-
|
2
|
-
# (c) 2005 Brian Amberg
|
3
|
-
# Please submit bugreports to mail@brian-amberg.de
|
1
|
+
require 'containers/heap'
|
4
2
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
include Enumerable
|
3
|
+
=begin rdoc
|
4
|
+
A Priority Queue is a data structure that behaves like a queue except that elements have an
|
5
|
+
associated priority. The #next and #pop methods return the item with the next highest priority.
|
9
6
|
|
10
|
-
|
11
|
-
|
12
|
-
# q = PriorityQueue.new
|
13
|
-
# q.length #=> 0
|
14
|
-
# q[0] = 1
|
15
|
-
# q.length #=> 1
|
16
|
-
attr_reader :length
|
17
|
-
|
18
|
-
# Create a new, empty PriorityQueue
|
19
|
-
def initialize
|
20
|
-
@nodes = Hash.new
|
21
|
-
@rootlist = nil
|
22
|
-
@min = nil
|
23
|
-
@length = 0
|
24
|
-
end
|
25
|
-
|
26
|
-
# Print a priority queue as a dot-graph. The output can be fed to dot from the
|
27
|
-
# vizgraph suite to create a tree depicting the internal datastructure.
|
28
|
-
def to_dot
|
29
|
-
r = ["digraph fibheap {"]
|
30
|
-
#r << @rootlist.to_dot.join("\n") if @rootlist
|
31
|
-
r << "ROOT -> #{@rootlist.dot_id};" if @rootlist
|
32
|
-
@nodes.to_a.sort.each do | (_, n) |
|
33
|
-
r << " #{n.dot_id} [label=\"#{n.key}: #{n.priority}\"];"
|
34
|
-
r << " #{n.dot_id} -> #{n.right.dot_id} [constraint=false];" if n.right# and n.dot_id < n.right.dot_id
|
35
|
-
r << " #{n.dot_id} -> #{n.left.dot_id} [constraint=false];" if n.left #and n.dot_id < n.left.dot_id
|
36
|
-
r << " #{n.dot_id} -> #{n.child.dot_id}" if n.child
|
37
|
-
end
|
38
|
-
r << "}"
|
39
|
-
r.join("\n")
|
40
|
-
r
|
41
|
-
end
|
42
|
-
|
43
|
-
# Call dot and gv displaying the datstructure
|
44
|
-
def display_dot
|
45
|
-
puts to_dot
|
46
|
-
system "echo '#{to_dot}' | twopi -Tps -Groot=ROOT -Goverlap=false> /tmp/dotfile.ps; gv /tmp/dotfile.ps"
|
47
|
-
end
|
48
|
-
|
49
|
-
# call-seq:
|
50
|
-
# [key] = priority
|
51
|
-
# change_priority(key, priority)
|
52
|
-
# push(key, priority)
|
53
|
-
#
|
54
|
-
# Set the priority of a key.
|
55
|
-
#
|
56
|
-
# q = PriorityQueue.new
|
57
|
-
# q["car"] = 50
|
58
|
-
# q["train"] = 50
|
59
|
-
# q["bike"] = 10
|
60
|
-
# q.min #=> ["bike", 10]
|
61
|
-
# q["car"] = 0
|
62
|
-
# q.min #=> ["car", 0]
|
63
|
-
def change_priority(key, priority)
|
64
|
-
return push(key, priority) unless @nodes[key]
|
65
|
-
|
66
|
-
n = @nodes[key]
|
67
|
-
if n.priority < priority # Priority was increased. Remove the node and reinsert.
|
68
|
-
self.delete(key)
|
69
|
-
self.push(key, priority);
|
70
|
-
return self
|
71
|
-
end
|
72
|
-
n.priority = priority;
|
73
|
-
@min = n if n.priority < @min.priority
|
74
|
-
|
75
|
-
return self if !n.parent or n.parent.priority <= n.priority # Already in rootlist or bigger than parent
|
76
|
-
begin # Cascading Cuts
|
77
|
-
p = n.parent
|
78
|
-
cut_node(n)
|
79
|
-
n = p
|
80
|
-
end while n.mark and n.parent
|
81
|
-
n.mark = true if n.parent
|
82
|
-
|
83
|
-
self
|
84
|
-
end
|
85
|
-
|
86
|
-
# Add an object to the queue.
|
87
|
-
def push(key, priority)
|
88
|
-
return change_priority(key, priority) if @nodes[key]
|
89
|
-
@nodes[key] = node = Node.new(key, priority)
|
90
|
-
@min = node if !@min or priority < @min.priority
|
91
|
-
if not @rootlist
|
92
|
-
@rootlist = node
|
93
|
-
node.left = node.right = node
|
94
|
-
else
|
95
|
-
node.left = @rootlist.left
|
96
|
-
node.right = @rootlist
|
97
|
-
@rootlist.left.right = node
|
98
|
-
@rootlist.left = node
|
99
|
-
end
|
100
|
-
@length += 1
|
101
|
-
self
|
102
|
-
end
|
103
|
-
|
104
|
-
# Returns true if the array is empty, false otherwise.
|
105
|
-
def empty?
|
106
|
-
@rootlist.nil?
|
107
|
-
end
|
108
|
-
|
109
|
-
# call-seq:
|
110
|
-
# [key] -> priority
|
111
|
-
#
|
112
|
-
# Return the priority of a key or nil if the key is not in the queue.
|
113
|
-
#
|
114
|
-
# q = PriorityQueue.new
|
115
|
-
# (0..10).each do | i | q[i.to_s] = i end
|
116
|
-
# q["5"] #=> 5
|
117
|
-
# q[5] #=> nil
|
118
|
-
def [](key)
|
119
|
-
@nodes[key] and @nodes[key].priority
|
120
|
-
end
|
121
|
-
|
122
|
-
# call-seq:
|
123
|
-
# has_key? key -> boolean
|
124
|
-
#
|
125
|
-
# Return false if the key is not in the queue, true otherwise.
|
126
|
-
#
|
127
|
-
# q = PriorityQueue.new
|
128
|
-
# (0..10).each do | i | q[i.to_s] = i end
|
129
|
-
# q.has_key("5") #=> true
|
130
|
-
# q.has_key(5) #=> false
|
131
|
-
def has_key?(key)
|
132
|
-
@nodes.has_key?(key)
|
133
|
-
end
|
134
|
-
|
135
|
-
alias :[]= :push
|
136
|
-
|
137
|
-
# Call the given block with each [key, priority] pair in the queue
|
138
|
-
#
|
139
|
-
# Beware: Changing the queue in the block may lead to unwanted behaviour and
|
140
|
-
# even infinite loops.
|
141
|
-
def each
|
142
|
-
@nodes.each do | key, node |
|
143
|
-
yield(key, node.priority)
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
# call-seq:
|
148
|
-
# min -> [object, priority]
|
149
|
-
#
|
150
|
-
# Return the pair [object, priority] with minimal priority or nil when the
|
151
|
-
# queue is empty.
|
152
|
-
#
|
153
|
-
# q = PriorityQueue.new
|
154
|
-
# q["a"] = 10
|
155
|
-
# q["b"] = 20
|
156
|
-
# q.min #=> ["a", 10]
|
157
|
-
# q.delete_min #=> ["a", 10]
|
158
|
-
# q.min #=> ["b", 20]
|
159
|
-
# q.delete_min #=> ["b", 20]
|
160
|
-
# q.min #=> nil
|
161
|
-
def min
|
162
|
-
[@min.key, @min.priority] rescue nil
|
163
|
-
end
|
164
|
-
|
165
|
-
# call-seq:
|
166
|
-
# min_key -> object
|
167
|
-
#
|
168
|
-
# Return the key that has the minimal priority or nil when the queue is empty.
|
169
|
-
#
|
170
|
-
# q = PriorityQueue.new
|
171
|
-
# q["a"] = 10
|
172
|
-
# q["b"] = 20
|
173
|
-
# q.min_key #=> "a"
|
174
|
-
# q.delete_min #=> ["a", 10]
|
175
|
-
# q.min_key #=> "b"
|
176
|
-
# q.delete_min #=> ["b", 20]
|
177
|
-
# q.min_key #=> nil
|
178
|
-
def min_key
|
179
|
-
@min.key rescue nil
|
180
|
-
end
|
181
|
-
|
182
|
-
# call-seq:
|
183
|
-
# min_priority -> priority
|
184
|
-
#
|
185
|
-
# Return the minimal priority or nil when the queue is empty.
|
186
|
-
#
|
187
|
-
# q = PriorityQueue.new
|
188
|
-
# q["a"] = 10
|
189
|
-
# q["b"] = 20
|
190
|
-
# q.min_priority #=> 10
|
191
|
-
# q.delete_min #=> ["a", 10]
|
192
|
-
# q.min_priority #=> 20
|
193
|
-
# q.delete_min #=> ["b", 20]
|
194
|
-
# q.min_priority #=> nil
|
195
|
-
def min_priority
|
196
|
-
@min.priority rescue nil
|
197
|
-
end
|
198
|
-
|
199
|
-
# call-seq:
|
200
|
-
# delete(key) -> [key, priority]
|
201
|
-
# delete(key) -> nil
|
202
|
-
#
|
203
|
-
# Delete a key from the priority queue. Returns nil when the key was not in
|
204
|
-
# the queue and [key, priority] otherwise.
|
205
|
-
#
|
206
|
-
# q = PriorityQueue.new
|
207
|
-
# (0..10).each do | i | q[i.to_s] = i end
|
208
|
-
# q.delete(5) #=> ["5", 5]
|
209
|
-
# q.delete(5) #=> nil
|
210
|
-
def delete(key)
|
211
|
-
return nil unless n = @nodes.delete(key)
|
212
|
-
|
213
|
-
if n.child
|
214
|
-
c = n.child
|
215
|
-
e = n.child
|
216
|
-
begin
|
217
|
-
r = c.right
|
218
|
-
cut_node(c)
|
219
|
-
c = r
|
220
|
-
end while c != e
|
221
|
-
end
|
222
|
-
cut_node(n) if n.parent
|
223
|
-
|
224
|
-
if n == n.right
|
225
|
-
@min = nil;
|
226
|
-
@rootlist = nil;
|
227
|
-
else
|
228
|
-
@rootlist = n.right if @rootlist == n
|
229
|
-
if @min == n
|
230
|
-
n1 = n.right
|
231
|
-
@min = n1
|
232
|
-
begin
|
233
|
-
@min = n1 if n1.priority < @min.priority
|
234
|
-
n1 = n1.right
|
235
|
-
end while(n1 != n);
|
236
|
-
end
|
237
|
-
n.right.left = n.left
|
238
|
-
n.left.right = n.right
|
239
|
-
n.left = n
|
240
|
-
n.right = n
|
241
|
-
end
|
242
|
-
@length -= 1
|
243
|
-
return [n.key, n.priority]
|
244
|
-
end
|
245
|
-
|
246
|
-
# call-seq:
|
247
|
-
# delete_min_return_key -> key
|
248
|
-
#
|
249
|
-
# Delete key with minimal priority and return the key
|
250
|
-
#
|
251
|
-
# q = PriorityQueue.new
|
252
|
-
# q["a"] = 1
|
253
|
-
# q["b"] = 0
|
254
|
-
# q.delete_min_return_key #=> "b"
|
255
|
-
# q.delete_min_return_key #=> "a"
|
256
|
-
# q.delete_min_return_key #=> nil
|
257
|
-
def delete_min_return_key
|
258
|
-
delete_min[0] rescue nil
|
259
|
-
end
|
260
|
-
|
261
|
-
# call-seq:
|
262
|
-
# delete_min_return_priority -> priority
|
263
|
-
#
|
264
|
-
# Delete key with minimal priority and return the priority value
|
265
|
-
#
|
266
|
-
# q = PriorityQueue.new
|
267
|
-
# q["a"] = 1
|
268
|
-
# q["b"] = 0
|
269
|
-
# q.delete_min_return_priority #=> 0
|
270
|
-
# q.delete_min_return_priority #=> 1
|
271
|
-
# q.delete_min_return_priority #=> nil
|
272
|
-
def delete_min_return_priority
|
273
|
-
delete_min[1] rescue nil
|
274
|
-
end
|
275
|
-
|
276
|
-
# call-seq:
|
277
|
-
# delete_min -> [key, priority]
|
278
|
-
#
|
279
|
-
# Delete key with minimal priority and return [key, priority]
|
280
|
-
#
|
281
|
-
# q = PriorityQueue.new
|
282
|
-
# q["a"] = 1
|
283
|
-
# q["b"] = 0
|
284
|
-
# q.delete_min #=> ["b", 0]
|
285
|
-
# q.delete_min #=> ["a", 1]
|
286
|
-
# q.delete_min #=> nil
|
287
|
-
def delete_min
|
288
|
-
return nil if self.empty?
|
289
|
-
result = self.min
|
290
|
-
|
291
|
-
@nodes.delete(@min.key)
|
292
|
-
|
293
|
-
if @length == 1
|
294
|
-
@rootlist = @min = nil
|
295
|
-
@length = 0
|
296
|
-
else
|
297
|
-
min = @min
|
298
|
-
if @min == @rootlist # If the rootlist is anchored at the minimum, shift to the right
|
299
|
-
if @rootlist == @rootlist.right
|
300
|
-
@rootlist = @min = nil
|
301
|
-
else
|
302
|
-
@rootlist = @min = @min.right
|
303
|
-
end
|
304
|
-
end
|
305
|
-
min.left.right = min.right;
|
306
|
-
min.right.left = min.left;
|
307
|
-
min.left = min.right = min;
|
308
|
-
if min.child
|
309
|
-
# Kinder und Eltern trennen, Markierung aufheben
|
310
|
-
n = min.child;
|
311
|
-
begin
|
312
|
-
n.parent = nil;
|
313
|
-
n.mark = false;
|
314
|
-
n = n.right;
|
315
|
-
end while n != min.child
|
316
|
-
|
317
|
-
# Kinder einf�gen
|
318
|
-
if @rootlist
|
319
|
-
l1 = @rootlist.left
|
320
|
-
l2 = n.left
|
321
|
-
|
322
|
-
l1.right = n
|
323
|
-
n.left = l1
|
324
|
-
l2.right = @rootlist
|
325
|
-
@rootlist.left = l2
|
326
|
-
else
|
327
|
-
@rootlist = n
|
328
|
-
end
|
329
|
-
end
|
330
|
-
|
331
|
-
# Gr��e anpassen
|
332
|
-
@length -= 1
|
333
|
-
|
334
|
-
# Wieder aufh�bschen
|
335
|
-
consolidate
|
336
|
-
end
|
337
|
-
|
338
|
-
result
|
339
|
-
end
|
340
|
-
|
341
|
-
# Returns a string representation of the priority queue.
|
342
|
-
def inspect
|
343
|
-
"<PriorityQueue: #{@nodes.map{|(_, n)| [n.key, n.priority]}.sort_by{|(_,p)|p}.inspect}>"
|
344
|
-
end
|
345
|
-
|
346
|
-
def initialize_copy(copy)
|
347
|
-
copy_nodes = @nodes
|
348
|
-
@nodes = {}
|
349
|
-
|
350
|
-
copy_nodes.each do | (_, cn) |
|
351
|
-
n = @nodes[cn.key] = Node.new(cn.key, cn.priority)
|
352
|
-
n.mark = cn.mark
|
353
|
-
n.degree = cn.degree
|
354
|
-
end
|
355
|
-
|
356
|
-
copy_nodes.each do | (_, cn) |
|
357
|
-
n = @nodes[cn.key]
|
358
|
-
n.left = @nodes[cn.left.key] if cn.left
|
359
|
-
n.right = @nodes[cn.right.key] if cn.right
|
360
|
-
n.parent = @nodes[cn.parent.key] if cn.parent
|
361
|
-
n.child = @nodes[cn.child.key] if cn.child
|
362
|
-
end
|
363
|
-
@rootlist = @nodes[@rootlist.key] if @rootlist
|
364
|
-
@min = @nodes[@min.key] if @min
|
365
|
-
self
|
366
|
-
end
|
367
|
-
|
368
|
-
# Node class used internally
|
369
|
-
class Node # :nodoc:
|
370
|
-
attr_accessor :parent, :child, :left, :right, :key, :priority, :degree, :mark
|
371
|
-
|
372
|
-
def child=(c)
|
373
|
-
raise "Circular Child" if c == self
|
374
|
-
raise "Child is neighbour" if c == self.right
|
375
|
-
raise "Child is neighbour" if c == self.left
|
376
|
-
@child = c
|
377
|
-
end
|
378
|
-
|
379
|
-
def to_dot(only_down = false, known_nodes = [])
|
380
|
-
p known_nodes.map { | n | n.dot_id }
|
381
|
-
p self.dot_id
|
382
|
-
result = []
|
383
|
-
if only_down
|
384
|
-
raise "Circular #{caller.inspect}" if known_nodes.include?(self)
|
385
|
-
known_nodes << self
|
386
|
-
|
387
|
-
result << "#{dot_id} [label=\"#{@key}: #{@priority}\"];"
|
388
|
-
l = " "
|
389
|
-
#l << "#{@left.dot_id} <- #{dot_id}; " if @left
|
390
|
-
l << "#{dot_id} -> #{@left.dot_id} [constraint=false]; " if @left and @left.dot_id < self.dot_id
|
391
|
-
l << "#{dot_id} -> #{@right.dot_id} [constraint=false];\t\t\t\t/*neighbours*/" if @right and @right.dot_id <= self.dot_id
|
392
|
-
result << l
|
393
|
-
result << " #{dot_id} -> #{@child.dot_id}; //child" if @child
|
394
|
-
result << @child.to_dot(false, known_nodes) if @child
|
395
|
-
else
|
396
|
-
n = self
|
397
|
-
begin
|
398
|
-
result.concat(n.to_dot(true, known_nodes))
|
399
|
-
n = n.right
|
400
|
-
end while n != self
|
401
|
-
end
|
402
|
-
result.flatten.map{|r| " " << r}
|
403
|
-
end
|
404
|
-
|
405
|
-
def dot_id
|
406
|
-
"N#{@key}"
|
407
|
-
end
|
408
|
-
|
409
|
-
def initialize(key, priority)
|
410
|
-
@key = key; @priority = priority; @degree = 0
|
411
|
-
end
|
412
|
-
end
|
7
|
+
Priority Queues are often used in graph problems, such as Dijkstra's Algorithm for shortest
|
8
|
+
path, and the A* search algorithm for shortest path.
|
413
9
|
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
return self unless n.parent
|
456
|
-
n.parent.degree -= 1
|
457
|
-
if n.parent.child == n
|
458
|
-
if n.right == n
|
459
|
-
n.parent.child = nil
|
460
|
-
else
|
461
|
-
n.parent.child = n.right;
|
462
|
-
end
|
463
|
-
end
|
464
|
-
n.parent = nil
|
465
|
-
n.right.left = n.left
|
466
|
-
n.left.right = n.right
|
467
|
-
|
468
|
-
n.right = @rootlist
|
469
|
-
n.left = @rootlist.left
|
470
|
-
@rootlist.left.right = n
|
471
|
-
@rootlist.left = n
|
472
|
-
|
473
|
-
n.mark = false
|
474
|
-
|
475
|
-
return self
|
476
|
-
end
|
477
|
-
|
478
|
-
# Does not change length
|
479
|
-
def insert_tree(tree)
|
480
|
-
if @rootlist == nil
|
481
|
-
@rootlist = @min = tree
|
482
|
-
else
|
483
|
-
l = @rootlist.left
|
484
|
-
l.right = tree
|
485
|
-
@rootlist.left = tree
|
486
|
-
tree.left = l
|
487
|
-
tree.right = @rootlist
|
488
|
-
@min = tree if tree.priority < @min.priority
|
489
|
-
end
|
490
|
-
self
|
491
|
-
end
|
492
|
-
|
493
|
-
def consolidate
|
494
|
-
return self if self.empty?
|
495
|
-
array_size = (2.0 * Math.log(self.length) / Math.log(2) + 1.0).ceil
|
496
|
-
tree_by_degree = Array.new(array_size)
|
497
|
-
|
498
|
-
while n = delete_first
|
499
|
-
while n1 = tree_by_degree[n.degree]
|
500
|
-
tree_by_degree[n.degree] = nil;
|
501
|
-
n = link_nodes(n, n1);
|
502
|
-
end
|
503
|
-
tree_by_degree[n.degree] = n;
|
504
|
-
end
|
10
|
+
This container is implemented using the Fibonacci heap included in the Collections library.
|
11
|
+
=end
|
12
|
+
class Containers::PriorityQueue
|
13
|
+
include Enumerable
|
14
|
+
|
15
|
+
# Create a new, empty PriorityQueue
|
16
|
+
def initialize(&block)
|
17
|
+
# We default to a priority queue that returns the largest value
|
18
|
+
block ||= lambda { |x, y| (x <=> y) == 1 }
|
19
|
+
@heap = Containers::Heap.new(&block)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns the number of elements in the queue.
|
23
|
+
#
|
24
|
+
# q = Containers::PriorityQueue.new
|
25
|
+
# q.size #=> 0
|
26
|
+
# q.push("Alaska", 1)
|
27
|
+
# q.size #=> 1
|
28
|
+
def size
|
29
|
+
@heap.size
|
30
|
+
end
|
31
|
+
alias_method :length, :size
|
32
|
+
|
33
|
+
# Add an object to the queue with associated priority.
|
34
|
+
#
|
35
|
+
# q = Containers::PriorityQueue.new
|
36
|
+
# q.push("Alaska", 1)
|
37
|
+
# q.pop #=> "Alaska"
|
38
|
+
def push(object, priority)
|
39
|
+
@heap.push(priority, object)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Clears all the items in the queue.
|
43
|
+
def clear
|
44
|
+
@heap.clear
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns true if the queue is empty, false otherwise.
|
48
|
+
def empty?
|
49
|
+
@heap.empty?
|
50
|
+
end
|
505
51
|
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
52
|
+
# call-seq:
|
53
|
+
# has_priority? priority -> boolean
|
54
|
+
#
|
55
|
+
# Return true if the priority is in the queue, false otherwise.
|
56
|
+
#
|
57
|
+
# q = PriorityQueue.new
|
58
|
+
# q.push("Alaska", 1)
|
59
|
+
#
|
60
|
+
# q.has_priority?(1) #=> true
|
61
|
+
# q.has_priority?(2) #=> false
|
62
|
+
def has_priority?(priority)
|
63
|
+
@heap.has_key?(priority)
|
64
|
+
end
|
65
|
+
|
66
|
+
# call-seq:
|
67
|
+
# next -> object
|
68
|
+
#
|
69
|
+
# Return the object with the next highest priority, but does not remove it
|
70
|
+
#
|
71
|
+
# q = Containers::PriorityQueue.new
|
72
|
+
# q.push("Alaska", 50)
|
73
|
+
# q.push("Delaware", 30)
|
74
|
+
# q.push("Georgia", 35)
|
75
|
+
# q.next #=> "Alaska"
|
76
|
+
def next
|
77
|
+
@heap.next
|
513
78
|
end
|
79
|
+
|
80
|
+
# call-seq:
|
81
|
+
# pop -> object
|
82
|
+
#
|
83
|
+
# Return the object with the next highest priority and removes it from the queue
|
84
|
+
#
|
85
|
+
# q = Containers::PriorityQueue.new
|
86
|
+
# q.push("Alaska", 50)
|
87
|
+
# q.push("Delaware", 30)
|
88
|
+
# q.push("Georgia", 35)
|
89
|
+
# q.pop #=> "Alaska"
|
90
|
+
# q.size #=> 2
|
91
|
+
def pop
|
92
|
+
@heap.pop
|
93
|
+
end
|
94
|
+
alias_method :next!, :pop
|
95
|
+
|
96
|
+
# call-seq:
|
97
|
+
# delete(priority) -> object
|
98
|
+
# delete(priority) -> nil
|
99
|
+
#
|
100
|
+
# Delete an object with specified priority from the queue. If there are duplicates, an
|
101
|
+
# arbitrary object with that priority is deleted and returned. Returns nil if there are
|
102
|
+
# no objects with the priority.
|
103
|
+
#
|
104
|
+
# q = PriorityQueue.new
|
105
|
+
# q.push("Alaska", 50)
|
106
|
+
# q.push("Delaware", 30)
|
107
|
+
# q.delete(50) #=> "Alaska"
|
108
|
+
# q.delete(10) #=> nil
|
109
|
+
def delete(priority)
|
110
|
+
@heap.delete(priority)
|
111
|
+
end
|
112
|
+
|
514
113
|
end
|