stakach-algorithms 1.0.4 → 1.0.5

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.
@@ -1,507 +1,507 @@
1
- =begin rdoc
2
- A Heap is a container that satisfies the heap property that nodes are
3
- always smaller in value than their parent node.
4
-
5
- The Algorithms::Containers::Heap class is flexible and upon
6
- initialization, takes an optional block that determines how the items are
7
- ordered. Two versions that are included are the
8
- Algorithms::Containers::MaxHeap and Algorithms::Containers::MinHeap that
9
- return the largest and smallest items on each invocation, respectively.
10
-
11
- This library implements a Fibonacci heap, which allows O(1) complexity for
12
- most methods.
13
- =end
14
- module Algorithms
15
- module Containers
16
- class Heap
17
- include Enumerable
18
-
19
- # call-seq:
20
- # size -> int
21
- #
22
- # Return the number of elements in the heap.
23
- def size
24
- @size
25
- end
26
- alias_method :length, :size
27
-
28
- # call-seq:
29
- # Heap.new(optional_array) { |x, y| optional_comparison_fn }
30
- # -> new_heap
31
- #
32
- # If an optional array is passed, the entries in the array are inserted
33
- # into the heap with equal key and value fields. Also, an optional block
34
- # can be passed to define the function that maintains heap property. For
35
- # example, a min-heap can be created with:
36
- #
37
- # minheap = Heap.new { |x, y| (x <=> y) == -1 }
38
- # minheap.push(6)
39
- # minheap.push(10)
40
- # minheap.pop #=> 6
41
- #
42
- # Thus, smaller elements will be parent nodes. The heap defaults to a
43
- # min-heap if no block is given.
44
- def initialize(ary=[], &block)
45
- @compare_fn = block || lambda { |x, y| (x <=> y) == -1 }
46
- @next = nil
47
- @size = 0
48
- @stored = {}
49
-
50
- ary.each { |n| push(n) } unless ary.empty?
51
- end
52
-
53
- # call-seq:
54
- # push(key, value) -> value
55
- # push(value) -> value
56
- #
57
- # Inserts an item with a given key into the heap. If only one parameter
58
- # is given, the key is set to the value.
59
- #
60
- # Complexity: O(1)
61
- #
62
- # heap = MinHeap.new
63
- # heap.push(1, "Cat")
64
- # heap.push(2)
65
- # heap.pop #=> "Cat"
66
- # heap.pop #=> 2
67
- def push(key, value=key)
68
- raise ArgumentError, "Heap keys must not be nil." unless key
69
- node = Node.new(key, value)
70
- # Add new node to the left of the @next node
71
- if @next
72
- node.right = @next
73
- node.left = @next.left
74
- node.left.right = node
75
- @next.left = node
76
- if @compare_fn[key, @next.key]
77
- @next = node
78
- end
79
- else
80
- @next = node
81
- end
82
- @size += 1
83
-
84
- arr = []
85
- w = @next.right
86
- until w == @next do
87
- arr << w.value
88
- w = w.right
89
- end
90
- arr << @next.value
91
- @stored[key] ||= []
92
- @stored[key] << node
93
- value
94
- end
95
- alias_method :<<, :push
96
-
97
- # call-seq:
98
- # has_key?(key) -> true or false
99
- #
100
- # Returns true if heap contains the key.
101
- #
102
- # Complexity: O(1)
103
- #
104
- # minheap = MinHeap.new([1, 2])
105
- # minheap.has_key?(2) #=> true
106
- # minheap.has_key?(4) #=> false
107
- def has_key?(key)
108
- @stored[key] && !@stored[key].empty? ? true : false
109
- end
110
-
111
- # call-seq:
112
- # next -> value
113
- # next -> nil
114
- #
115
- # Returns the value of the next item in heap order, but does not remove
116
- # it.
117
- #
118
- # Complexity: O(1)
119
- #
120
- # minheap = MinHeap.new([1, 2])
121
- # minheap.next #=> 1
122
- # minheap.size #=> 2
123
- def next
124
- @next && @next.value
125
- end
126
-
127
- # call-seq:
128
- # clear -> nil
129
- #
130
- # Removes all elements from the heap, destructively.
131
- #
132
- # Complexity: O(1)
133
- #
134
- def clear
135
- @next = nil
136
- @size = 0
137
- @stored = {}
138
- nil
139
- end
140
-
141
- # call-seq:
142
- # empty? -> true or false
143
- #
144
- # Returns true if the heap is empty, false otherwise.
145
- def empty?
146
- @next.nil?
147
- end
148
-
149
- # call-seq:
150
- # merge!(otherheap) -> merged_heap
151
- #
152
- # Does a shallow merge of all the nodes in the other heap.
153
- #
154
- # Complexity: O(1)
155
- #
156
- # heap = MinHeap.new([5, 6, 7, 8])
157
- # otherheap = MinHeap.new([1, 2, 3, 4])
158
- # heap.merge!(otherheap)
159
- # heap.size #=> 8
160
- # heap.pop #=> 1
161
- def merge!(otherheap)
162
- raise ArgumentError, "Trying to merge a heap with something not a heap" unless otherheap.kind_of? Heap
163
- other_root = otherheap.instance_variable_get("@next")
164
- if other_root
165
- @stored = @stored.merge(otherheap.instance_variable_get("@stored")) { |key, a, b| (a << b).flatten }
166
- # Insert othernode's @next node to the left of current @next
167
- @next.left.right = other_root
168
- ol = other_root.left
169
- other_root.left = @next.left
170
- ol.right = @next
171
- @next.left = ol
172
-
173
- @next = other_root if @compare_fn[other_root.key, @next.key]
174
- end
175
- @size += otherheap.size
176
- end
177
-
178
- # call-seq:
179
- # pop -> value
180
- # pop -> nil
181
- #
182
- # Returns the value of the next item in heap order and removes it from
183
- # the heap.
184
- #
185
- # Complexity: O(1)
186
- #
187
- # minheap = MinHeap.new([1, 2])
188
- # minheap.pop #=> 1
189
- # minheap.size #=> 1
190
- def pop
191
- return nil unless @next
192
- popped = @next
193
- if @size == 1
194
- clear
195
- return popped.value
196
- end
197
- # Merge the popped's children into root node
198
- if @next.child
199
- @next.child.parent = nil
200
-
201
- # get rid of parent
202
- sibling = @next.child.right
203
- until sibling == @next.child
204
- sibling.parent = nil
205
- sibling = sibling.right
206
- end
207
-
208
- # Merge the children into the root. If @next is the only root node,
209
- # make its child the @next node
210
- if @next.right == @next
211
- @next = @next.child
212
- else
213
- next_left, next_right = @next.left, @next.right
214
- current_child = @next.child
215
- @next.right.left = current_child
216
- @next.left.right = current_child.right
217
- current_child.right.left = next_left
218
- current_child.right = next_right
219
- @next = @next.right
220
- end
221
- else
222
- @next.left.right = @next.right
223
- @next.right.left = @next.left
224
- @next = @next.right
225
- end
226
- consolidate
227
-
228
- unless @stored[popped.key].delete(popped)
229
- raise "Couldn't delete node from stored nodes hash"
230
- end
231
- @size -= 1
232
-
233
- popped.value
234
- end
235
- alias_method :next!, :pop
236
-
237
- # call-seq:
238
- # change_key(key, new_key) -> [new_key, value]
239
- # change_key(key, new_key) -> nil
240
- #
241
- # Changes the key from one to another. Doing so must not violate the
242
- # heap property or an exception will be raised. If the key is found, an
243
- # array containing the new key and value pair is returned, otherwise
244
- # nil is returned.
245
- #
246
- # In the case of duplicate keys, an arbitrary key is changed. This will
247
- # be investigated more in the future.
248
- #
249
- # Complexity: amortized O(1)
250
- #
251
- # minheap = MinHeap.new([1, 2])
252
- # minheap.change_key(2, 3) #=> raise error since we can't increase the value in a min-heap
253
- # minheap.change_key(2, 0) #=> [0, 2]
254
- # minheap.pop #=> 2
255
- # minheap.pop #=> 1
256
- def change_key(key, new_key, delete=false)
257
- return if @stored[key].nil? || @stored[key].empty? || (key == new_key)
258
-
259
- # Must maintain heap property
260
- raise "Changing this key would not maintain heap property!" unless (delete || @compare_fn[new_key, key])
261
- node = @stored[key].shift
262
- if node
263
- node.key = new_key
264
- @stored[new_key] ||= []
265
- @stored[new_key] << node
266
- parent = node.parent
267
- if parent
268
- # if heap property is violated
269
- if delete || @compare_fn[new_key, parent.key]
270
- cut(node, parent)
271
- cascading_cut(parent)
272
- end
273
- end
274
- if delete || @compare_fn[node.key, @next.key]
275
- @next = node
276
- end
277
- return [node.key, node.value]
278
- end
279
- nil
280
- end
281
-
282
- # call-seq:
283
- # delete(key) -> value
284
- # delete(key) -> nil
285
- #
286
- # Deletes the item with associated key and returns it. nil is returned
287
- # if the keyis not found. In the case of nodes with duplicate keys, an
288
- # arbitrary one is deleted.
289
- #
290
- # Complexity: amortized O(log n)
291
- #
292
- # minheap = MinHeap.new([1, 2])
293
- # minheap.delete(1) #=> 1
294
- # minheap.size #=> 1
295
- def delete(key)
296
- pop if change_key(key, nil, true)
297
- end
298
-
299
- # Node class used internally
300
- class Node # :nodoc:
301
- attr_accessor :parent, :child, :left, :right, :key, :value, :degree, :marked
302
-
303
- def initialize(key, value)
304
- @key = key
305
- @value = value
306
- @degree = 0
307
- @marked = false
308
- @right = self
309
- @left = self
310
- end
311
-
312
- def marked?
313
- @marked == true
314
- end
315
-
316
- end
317
-
318
- # make node a child of a parent node
319
- def link_nodes(child, parent)
320
- # link the child's siblings
321
- child.left.right = child.right
322
- child.right.left = child.left
323
-
324
- child.parent = parent
325
-
326
- # if parent doesn't have children, make new child its only child
327
- if parent.child.nil?
328
- parent.child = child.right = child.left = child
329
- else # otherwise insert new child into parent's children list
330
- current_child = parent.child
331
- child.left = current_child
332
- child.right = current_child.right
333
- current_child.right.left = child
334
- current_child.right = child
335
- end
336
- parent.degree += 1
337
- child.marked = false
338
- end
339
- private :link_nodes
340
-
341
- # Makes sure the structure does not contain nodes in the root list with
342
- # equal degrees
343
- def consolidate
344
- roots = []
345
- root = @next
346
- min = root
347
- # find the nodes in the list
348
- loop do
349
- roots << root
350
- root = root.right
351
- break if root == @next
352
- end
353
- degrees = []
354
- roots.each do |root|
355
- min = root if @compare_fn[root.key, min.key]
356
- # check if we need to merge
357
- if degrees[root.degree].nil? # no other node with the same degree
358
- degrees[root.degree] = root
359
- next
360
- else # there is another node with the same degree, consolidate them
361
- degree = root.degree
362
- until degrees[degree].nil? do
363
- other_root_with_degree = degrees[degree]
364
- if @compare_fn[root.key, other_root_with_degree.key] # determine which node is the parent, which one is the child
365
- smaller, larger = root, other_root_with_degree
366
- else
367
- smaller, larger = other_root_with_degree, root
368
- end
369
- link_nodes(larger, smaller)
370
- degrees[degree] = nil
371
- root = smaller
372
- degree += 1
373
- end
374
- degrees[degree] = root
375
- min = root if min.key == root.key # this fixes a bug with duplicate keys not being in the right order
376
- end
377
- end
378
- @next = min
379
- end
380
- private :consolidate
381
-
382
- def cascading_cut(node)
383
- p = node.parent
384
- if p
385
- if node.marked?
386
- cut(node, p)
387
- cascading_cut(p)
388
- else
389
- node.marked = true
390
- end
391
- end
392
- end
393
- private :cascading_cut
394
-
395
- # remove x from y's children and add x to the root list
396
- def cut(x, y)
397
- x.left.right = x.right
398
- x.right.left = x.left
399
- y.degree -= 1
400
- if (y.degree == 0)
401
- y.child = nil
402
- elsif (y.child == x)
403
- y.child = x.right
404
- end
405
- x.right = @next
406
- x.left = @next.left
407
- @next.left = x
408
- x.left.right = x
409
- x.parent = nil
410
- x.marked = false
411
- end
412
- private :cut
413
-
414
- end
415
-
416
- # A MaxHeap is a heap where the items are returned in descending order of
417
- # key value.
418
- class MaxHeap < Heap
419
-
420
- # call-seq:
421
- # MaxHeap.new(ary) -> new_heap
422
- #
423
- # Creates a new MaxHeap with an optional array parameter of items to
424
- # insert into the heap. A MaxHeap is created by calling
425
- # Heap.new { |x, y| (x <=> y) == 1 }, so this is a convenience class.
426
- #
427
- # maxheap = MaxHeap.new([1, 2, 3, 4])
428
- # maxheap.pop #=> 4
429
- # maxheap.pop #=> 3
430
- def initialize(ary=[])
431
- super(ary) { |x, y| (x <=> y) == 1 }
432
- end
433
-
434
- # call-seq:
435
- # max -> value
436
- # max -> nil
437
- #
438
- # Returns the item with the largest key, but does not remove it from the
439
- # heap.
440
- #
441
- # maxheap = MaxHeap.new([1, 2, 3, 4])
442
- # maxheap.max #=> 4
443
- def max
444
- self.next
445
- end
446
-
447
- # call-seq:
448
- # max! -> value
449
- # max! -> nil
450
- #
451
- # Returns the item with the largest key and removes it from the heap.
452
- #
453
- # maxheap = MaxHeap.new([1, 2, 3, 4])
454
- # maxheap.max! #=> 4
455
- # maxheap.size #=> 3
456
- def max!
457
- self.pop
458
- end
459
- end
460
-
461
- # A MinHeap is a heap where the items are returned in ascending order of
462
- # key value.
463
- class MinHeap < Heap
464
-
465
- # call-seq:
466
- # MinHeap.new(ary) -> new_heap
467
- #
468
- # Creates a new MinHeap with an optional array parameter of items to
469
- # insert into the heap.
470
- # A MinHeap is created by calling Heap.new { |x, y| (x <=> y) == -1 },
471
- # so this is a convenience class.
472
- #
473
- # minheap = MinHeap.new([1, 2, 3, 4])
474
- # minheap.pop #=> 1
475
- # minheap.pop #=> 2
476
- def initialize(ary=[])
477
- super(ary) { |x, y| (x <=> y) == -1 }
478
- end
479
-
480
- # call-seq:
481
- # min -> value
482
- # min -> nil
483
- #
484
- # Returns the item with the smallest key, but does not remove it from
485
- # the heap.
486
- #
487
- # minheap = MinHeap.new([1, 2, 3, 4])
488
- # minheap.min #=> 1
489
- def min
490
- self.next
491
- end
492
-
493
- # call-seq:
494
- # min! -> value
495
- # min! -> nil
496
- #
497
- # Returns the item with the smallest key and removes it from the heap.
498
- #
499
- # minheap = MinHeap.new([1, 2, 3, 4])
500
- # minheap.min! #=> 1
501
- # minheap.size #=> 3
502
- def min!
503
- self.pop
504
- end
505
- end
506
- end
1
+ =begin rdoc
2
+ A Heap is a container that satisfies the heap property that nodes are
3
+ always smaller in value than their parent node.
4
+
5
+ The Algorithms::Containers::Heap class is flexible and upon
6
+ initialization, takes an optional block that determines how the items are
7
+ ordered. Two versions that are included are the
8
+ Algorithms::Containers::MaxHeap and Algorithms::Containers::MinHeap that
9
+ return the largest and smallest items on each invocation, respectively.
10
+
11
+ This library implements a Fibonacci heap, which allows O(1) complexity for
12
+ most methods.
13
+ =end
14
+ module Algorithms
15
+ module Containers
16
+ class Heap
17
+ include Enumerable
18
+
19
+ # call-seq:
20
+ # size -> int
21
+ #
22
+ # Return the number of elements in the heap.
23
+ def size
24
+ @size
25
+ end
26
+ alias_method :length, :size
27
+
28
+ # call-seq:
29
+ # Heap.new(optional_array) { |x, y| optional_comparison_fn }
30
+ # -> new_heap
31
+ #
32
+ # If an optional array is passed, the entries in the array are inserted
33
+ # into the heap with equal key and value fields. Also, an optional block
34
+ # can be passed to define the function that maintains heap property. For
35
+ # example, a min-heap can be created with:
36
+ #
37
+ # minheap = Heap.new { |x, y| (x <=> y) == -1 }
38
+ # minheap.push(6)
39
+ # minheap.push(10)
40
+ # minheap.pop #=> 6
41
+ #
42
+ # Thus, smaller elements will be parent nodes. The heap defaults to a
43
+ # min-heap if no block is given.
44
+ def initialize(ary=[], &block)
45
+ @compare_fn = block || lambda { |x, y| (x <=> y) == -1 }
46
+ @next = nil
47
+ @size = 0
48
+ @stored = {}
49
+
50
+ ary.each { |n| push(n) } unless ary.empty?
51
+ end
52
+
53
+ # call-seq:
54
+ # push(key, value) -> value
55
+ # push(value) -> value
56
+ #
57
+ # Inserts an item with a given key into the heap. If only one parameter
58
+ # is given, the key is set to the value.
59
+ #
60
+ # Complexity: O(1)
61
+ #
62
+ # heap = MinHeap.new
63
+ # heap.push(1, "Cat")
64
+ # heap.push(2)
65
+ # heap.pop #=> "Cat"
66
+ # heap.pop #=> 2
67
+ def push(key, value=key)
68
+ raise ArgumentError, "Heap keys must not be nil." unless key
69
+ node = Node.new(key, value)
70
+ # Add new node to the left of the @next node
71
+ if @next
72
+ node.right = @next
73
+ node.left = @next.left
74
+ node.left.right = node
75
+ @next.left = node
76
+ if @compare_fn[key, @next.key]
77
+ @next = node
78
+ end
79
+ else
80
+ @next = node
81
+ end
82
+ @size += 1
83
+
84
+ arr = []
85
+ w = @next.right
86
+ until w == @next do
87
+ arr << w.value
88
+ w = w.right
89
+ end
90
+ arr << @next.value
91
+ @stored[key] ||= []
92
+ @stored[key] << node
93
+ value
94
+ end
95
+ alias_method :<<, :push
96
+
97
+ # call-seq:
98
+ # has_key?(key) -> true or false
99
+ #
100
+ # Returns true if heap contains the key.
101
+ #
102
+ # Complexity: O(1)
103
+ #
104
+ # minheap = MinHeap.new([1, 2])
105
+ # minheap.has_key?(2) #=> true
106
+ # minheap.has_key?(4) #=> false
107
+ def has_key?(key)
108
+ @stored[key] && !@stored[key].empty? ? true : false
109
+ end
110
+
111
+ # call-seq:
112
+ # next -> value
113
+ # next -> nil
114
+ #
115
+ # Returns the value of the next item in heap order, but does not remove
116
+ # it.
117
+ #
118
+ # Complexity: O(1)
119
+ #
120
+ # minheap = MinHeap.new([1, 2])
121
+ # minheap.next #=> 1
122
+ # minheap.size #=> 2
123
+ def next
124
+ @next && @next.value
125
+ end
126
+
127
+ # call-seq:
128
+ # clear -> nil
129
+ #
130
+ # Removes all elements from the heap, destructively.
131
+ #
132
+ # Complexity: O(1)
133
+ #
134
+ def clear
135
+ @next = nil
136
+ @size = 0
137
+ @stored = {}
138
+ nil
139
+ end
140
+
141
+ # call-seq:
142
+ # empty? -> true or false
143
+ #
144
+ # Returns true if the heap is empty, false otherwise.
145
+ def empty?
146
+ @next.nil?
147
+ end
148
+
149
+ # call-seq:
150
+ # merge!(otherheap) -> merged_heap
151
+ #
152
+ # Does a shallow merge of all the nodes in the other heap.
153
+ #
154
+ # Complexity: O(1)
155
+ #
156
+ # heap = MinHeap.new([5, 6, 7, 8])
157
+ # otherheap = MinHeap.new([1, 2, 3, 4])
158
+ # heap.merge!(otherheap)
159
+ # heap.size #=> 8
160
+ # heap.pop #=> 1
161
+ def merge!(otherheap)
162
+ raise ArgumentError, "Trying to merge a heap with something not a heap" unless otherheap.kind_of? Heap
163
+ other_root = otherheap.instance_variable_get("@next")
164
+ if other_root
165
+ @stored = @stored.merge(otherheap.instance_variable_get("@stored")) { |key, a, b| (a << b).flatten }
166
+ # Insert othernode's @next node to the left of current @next
167
+ @next.left.right = other_root
168
+ ol = other_root.left
169
+ other_root.left = @next.left
170
+ ol.right = @next
171
+ @next.left = ol
172
+
173
+ @next = other_root if @compare_fn[other_root.key, @next.key]
174
+ end
175
+ @size += otherheap.size
176
+ end
177
+
178
+ # call-seq:
179
+ # pop -> value
180
+ # pop -> nil
181
+ #
182
+ # Returns the value of the next item in heap order and removes it from
183
+ # the heap.
184
+ #
185
+ # Complexity: O(1)
186
+ #
187
+ # minheap = MinHeap.new([1, 2])
188
+ # minheap.pop #=> 1
189
+ # minheap.size #=> 1
190
+ def pop
191
+ return nil unless @next
192
+ popped = @next
193
+ if @size == 1
194
+ clear
195
+ return popped.value
196
+ end
197
+ # Merge the popped's children into root node
198
+ if @next.child
199
+ @next.child.parent = nil
200
+
201
+ # get rid of parent
202
+ sibling = @next.child.right
203
+ until sibling == @next.child
204
+ sibling.parent = nil
205
+ sibling = sibling.right
206
+ end
207
+
208
+ # Merge the children into the root. If @next is the only root node,
209
+ # make its child the @next node
210
+ if @next.right == @next
211
+ @next = @next.child
212
+ else
213
+ next_left, next_right = @next.left, @next.right
214
+ current_child = @next.child
215
+ @next.right.left = current_child
216
+ @next.left.right = current_child.right
217
+ current_child.right.left = next_left
218
+ current_child.right = next_right
219
+ @next = @next.right
220
+ end
221
+ else
222
+ @next.left.right = @next.right
223
+ @next.right.left = @next.left
224
+ @next = @next.right
225
+ end
226
+ consolidate
227
+
228
+ unless @stored[popped.key].delete(popped)
229
+ raise "Couldn't delete node from stored nodes hash"
230
+ end
231
+ @size -= 1
232
+
233
+ popped.value
234
+ end
235
+ alias_method :next!, :pop
236
+
237
+ # call-seq:
238
+ # change_key(key, new_key) -> [new_key, value]
239
+ # change_key(key, new_key) -> nil
240
+ #
241
+ # Changes the key from one to another. Doing so must not violate the
242
+ # heap property or an exception will be raised. If the key is found, an
243
+ # array containing the new key and value pair is returned, otherwise
244
+ # nil is returned.
245
+ #
246
+ # In the case of duplicate keys, an arbitrary key is changed. This will
247
+ # be investigated more in the future.
248
+ #
249
+ # Complexity: amortized O(1)
250
+ #
251
+ # minheap = MinHeap.new([1, 2])
252
+ # minheap.change_key(2, 3) #=> raise error since we can't increase the value in a min-heap
253
+ # minheap.change_key(2, 0) #=> [0, 2]
254
+ # minheap.pop #=> 2
255
+ # minheap.pop #=> 1
256
+ def change_key(key, new_key, delete=false)
257
+ return if @stored[key].nil? || @stored[key].empty? || (key == new_key)
258
+
259
+ # Must maintain heap property
260
+ raise "Changing this key would not maintain heap property!" unless (delete || @compare_fn[new_key, key])
261
+ node = @stored[key].shift
262
+ if node
263
+ node.key = new_key
264
+ @stored[new_key] ||= []
265
+ @stored[new_key] << node
266
+ parent = node.parent
267
+ if parent
268
+ # if heap property is violated
269
+ if delete || @compare_fn[new_key, parent.key]
270
+ cut(node, parent)
271
+ cascading_cut(parent)
272
+ end
273
+ end
274
+ if delete || @compare_fn[node.key, @next.key]
275
+ @next = node
276
+ end
277
+ return [node.key, node.value]
278
+ end
279
+ nil
280
+ end
281
+
282
+ # call-seq:
283
+ # delete(key) -> value
284
+ # delete(key) -> nil
285
+ #
286
+ # Deletes the item with associated key and returns it. nil is returned
287
+ # if the keyis not found. In the case of nodes with duplicate keys, an
288
+ # arbitrary one is deleted.
289
+ #
290
+ # Complexity: amortized O(log n)
291
+ #
292
+ # minheap = MinHeap.new([1, 2])
293
+ # minheap.delete(1) #=> 1
294
+ # minheap.size #=> 1
295
+ def delete(key)
296
+ pop if change_key(key, nil, true)
297
+ end
298
+
299
+ # Node class used internally
300
+ class Node # :nodoc:
301
+ attr_accessor :parent, :child, :left, :right, :key, :value, :degree, :marked
302
+
303
+ def initialize(key, value)
304
+ @key = key
305
+ @value = value
306
+ @degree = 0
307
+ @marked = false
308
+ @right = self
309
+ @left = self
310
+ end
311
+
312
+ def marked?
313
+ @marked == true
314
+ end
315
+
316
+ end
317
+
318
+ # make node a child of a parent node
319
+ def link_nodes(child, parent)
320
+ # link the child's siblings
321
+ child.left.right = child.right
322
+ child.right.left = child.left
323
+
324
+ child.parent = parent
325
+
326
+ # if parent doesn't have children, make new child its only child
327
+ if parent.child.nil?
328
+ parent.child = child.right = child.left = child
329
+ else # otherwise insert new child into parent's children list
330
+ current_child = parent.child
331
+ child.left = current_child
332
+ child.right = current_child.right
333
+ current_child.right.left = child
334
+ current_child.right = child
335
+ end
336
+ parent.degree += 1
337
+ child.marked = false
338
+ end
339
+ private :link_nodes
340
+
341
+ # Makes sure the structure does not contain nodes in the root list with
342
+ # equal degrees
343
+ def consolidate
344
+ roots = []
345
+ root = @next
346
+ min = root
347
+ # find the nodes in the list
348
+ loop do
349
+ roots << root
350
+ root = root.right
351
+ break if root == @next
352
+ end
353
+ degrees = []
354
+ roots.each do |root|
355
+ min = root if @compare_fn[root.key, min.key]
356
+ # check if we need to merge
357
+ if degrees[root.degree].nil? # no other node with the same degree
358
+ degrees[root.degree] = root
359
+ next
360
+ else # there is another node with the same degree, consolidate them
361
+ degree = root.degree
362
+ until degrees[degree].nil? do
363
+ other_root_with_degree = degrees[degree]
364
+ if @compare_fn[root.key, other_root_with_degree.key] # determine which node is the parent, which one is the child
365
+ smaller, larger = root, other_root_with_degree
366
+ else
367
+ smaller, larger = other_root_with_degree, root
368
+ end
369
+ link_nodes(larger, smaller)
370
+ degrees[degree] = nil
371
+ root = smaller
372
+ degree += 1
373
+ end
374
+ degrees[degree] = root
375
+ min = root if min.key == root.key # this fixes a bug with duplicate keys not being in the right order
376
+ end
377
+ end
378
+ @next = min
379
+ end
380
+ private :consolidate
381
+
382
+ def cascading_cut(node)
383
+ p = node.parent
384
+ if p
385
+ if node.marked?
386
+ cut(node, p)
387
+ cascading_cut(p)
388
+ else
389
+ node.marked = true
390
+ end
391
+ end
392
+ end
393
+ private :cascading_cut
394
+
395
+ # remove x from y's children and add x to the root list
396
+ def cut(x, y)
397
+ x.left.right = x.right
398
+ x.right.left = x.left
399
+ y.degree -= 1
400
+ if (y.degree == 0)
401
+ y.child = nil
402
+ elsif (y.child == x)
403
+ y.child = x.right
404
+ end
405
+ x.right = @next
406
+ x.left = @next.left
407
+ @next.left = x
408
+ x.left.right = x
409
+ x.parent = nil
410
+ x.marked = false
411
+ end
412
+ private :cut
413
+
414
+ end
415
+
416
+ # A MaxHeap is a heap where the items are returned in descending order of
417
+ # key value.
418
+ class MaxHeap < Heap
419
+
420
+ # call-seq:
421
+ # MaxHeap.new(ary) -> new_heap
422
+ #
423
+ # Creates a new MaxHeap with an optional array parameter of items to
424
+ # insert into the heap. A MaxHeap is created by calling
425
+ # Heap.new { |x, y| (x <=> y) == 1 }, so this is a convenience class.
426
+ #
427
+ # maxheap = MaxHeap.new([1, 2, 3, 4])
428
+ # maxheap.pop #=> 4
429
+ # maxheap.pop #=> 3
430
+ def initialize(ary=[])
431
+ super(ary) { |x, y| (x <=> y) == 1 }
432
+ end
433
+
434
+ # call-seq:
435
+ # max -> value
436
+ # max -> nil
437
+ #
438
+ # Returns the item with the largest key, but does not remove it from the
439
+ # heap.
440
+ #
441
+ # maxheap = MaxHeap.new([1, 2, 3, 4])
442
+ # maxheap.max #=> 4
443
+ def max
444
+ self.next
445
+ end
446
+
447
+ # call-seq:
448
+ # max! -> value
449
+ # max! -> nil
450
+ #
451
+ # Returns the item with the largest key and removes it from the heap.
452
+ #
453
+ # maxheap = MaxHeap.new([1, 2, 3, 4])
454
+ # maxheap.max! #=> 4
455
+ # maxheap.size #=> 3
456
+ def max!
457
+ self.pop
458
+ end
459
+ end
460
+
461
+ # A MinHeap is a heap where the items are returned in ascending order of
462
+ # key value.
463
+ class MinHeap < Heap
464
+
465
+ # call-seq:
466
+ # MinHeap.new(ary) -> new_heap
467
+ #
468
+ # Creates a new MinHeap with an optional array parameter of items to
469
+ # insert into the heap.
470
+ # A MinHeap is created by calling Heap.new { |x, y| (x <=> y) == -1 },
471
+ # so this is a convenience class.
472
+ #
473
+ # minheap = MinHeap.new([1, 2, 3, 4])
474
+ # minheap.pop #=> 1
475
+ # minheap.pop #=> 2
476
+ def initialize(ary=[])
477
+ super(ary) { |x, y| (x <=> y) == -1 }
478
+ end
479
+
480
+ # call-seq:
481
+ # min -> value
482
+ # min -> nil
483
+ #
484
+ # Returns the item with the smallest key, but does not remove it from
485
+ # the heap.
486
+ #
487
+ # minheap = MinHeap.new([1, 2, 3, 4])
488
+ # minheap.min #=> 1
489
+ def min
490
+ self.next
491
+ end
492
+
493
+ # call-seq:
494
+ # min! -> value
495
+ # min! -> nil
496
+ #
497
+ # Returns the item with the smallest key and removes it from the heap.
498
+ #
499
+ # minheap = MinHeap.new([1, 2, 3, 4])
500
+ # minheap.min! #=> 1
501
+ # minheap.size #=> 3
502
+ def min!
503
+ self.pop
504
+ end
505
+ end
506
+ end
507
507
  end