fibonacci_heap 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.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +239 -0
- data/lib/fibonacci_heap.rb +2 -0
- data/lib/fibonacci_heap/circular_doubly_linked_list.rb +84 -0
- data/lib/fibonacci_heap/heap.rb +239 -0
- data/lib/fibonacci_heap/node.rb +56 -0
- data/spec/fibonacci_heap/circular_doubly_linked_list_spec.rb +269 -0
- data/spec/fibonacci_heap/heap_spec.rb +311 -0
- data/spec/fibonacci_heap/node_spec.rb +29 -0
- data/spec/spec_helper.rb +19 -0
- metadata +73 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 9d5517c8d52edce5c8e372cd14457efe425f6a422fec8a02585221df8fa2f25a
|
4
|
+
data.tar.gz: 3a6e50318184cfa233fe840e91806a209379dccc5dde84f7739b8921e3d73712
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6a93299721795318e9cd8529e899d710a32fb2bfde25b825d31a37a1318e78afa62bece5d26d4010a3a1cfa8c6a6420c5754c47e14cc56f02f1a40545911c37a
|
7
|
+
data.tar.gz: 4790e9bbf353167b8a018e9e2a6b463a4159b5e5b0d434895e850466c495f55df7e92fc4744c2c25120c6474d4219382b428f905f82e56051d053db59710bd74
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2018 Paul Mucur
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,239 @@
|
|
1
|
+
# Fibonacci Heap [](https://travis-ci.org/mudge/fibonacci_heap)
|
2
|
+
|
3
|
+
A Ruby implementation of the [Fibonacci heap](https://en.wikipedia.org/wiki/Fibonacci_heap) data structure ideal for use as a priority queue with [Dijkstra's algorithm](https://en.wikipedia.org/wiki/Dijkstra's_algorithm#Using_a_priority_queue).
|
4
|
+
|
5
|
+
**Current version:** 0.1.0
|
6
|
+
**Supported Ruby versions:** 1.8.7, 1.9.2, 1.9.3, 2.0, 2.1, 2.2, 2.3
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
```
|
11
|
+
gem install fibonacci_heap -v '~> 0.1'
|
12
|
+
```
|
13
|
+
|
14
|
+
Or, in your `Gemfile`:
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
gem 'fibonacci_heap', '~> 0.1'
|
18
|
+
```
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
require 'fibonacci_heap'
|
24
|
+
|
25
|
+
heap = FibonacciHeap::Heap.new
|
26
|
+
foo = FibonacciHeap::Node.new(1, 'foo')
|
27
|
+
bar = FibonacciHeap::Node.new(0, 'bar')
|
28
|
+
heap.insert(foo)
|
29
|
+
heap.insert(bar)
|
30
|
+
heap.pop
|
31
|
+
#=> #<FibonacciHeap::Node key=0 value="bar">
|
32
|
+
```
|
33
|
+
|
34
|
+
## API Documentation
|
35
|
+
|
36
|
+
* [`FibonacciHeap::Heap`](#fibonacciheapheap)
|
37
|
+
* [`.new`](#fibonacciheapheapnew)
|
38
|
+
* [`#n`](#fibonacciheapheapn)
|
39
|
+
* [`#size`](#fibonacciheapheapn)
|
40
|
+
* [`#length`](#fibonacciheapheapn)
|
41
|
+
* [`#empty?`](#fibonacciheapheapempty)
|
42
|
+
* [`#min`](#fibonacciheapheapmin)
|
43
|
+
* [`#insert(x[, k])`](#fibonacciheapheapinsertx-k)
|
44
|
+
* [`#concat(h2)`](#fibonacciheapheapconcath2)
|
45
|
+
* [`#pop`](#fibonacciheapheappop)
|
46
|
+
* [`#decrease_key(x, k)`](#fibonacciheapheapdecrease_keyx-k)
|
47
|
+
* [`#delete(x)`](#fibonacciheapheapdeletex)
|
48
|
+
* [`FibonacciHeap::Node`](#fibonacciheapnode)
|
49
|
+
* [`new(key[, value])`](#fibonacciheapnodenewkey-value)
|
50
|
+
* [`key`](#fibonacciheapnodekey)
|
51
|
+
* [`value`](#fibonacciheapnodevalue)
|
52
|
+
* [`FibonacciHeap::InvalidKeyError`](#fibonacciheapinvalidkeyerror)
|
53
|
+
|
54
|
+
### `FibonacciHeap::Heap`
|
55
|
+
|
56
|
+
A Fibonacci Heap data structure.
|
57
|
+
|
58
|
+
A "mergeable heap" that supports several operations that run in
|
59
|
+
constant amortized time. Structured as a collection of rooted trees
|
60
|
+
that are min-heap ordered.
|
61
|
+
|
62
|
+
#### `FibonacciHeap::Heap.new`
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
heap = FibonacciHeap::Heap.new
|
66
|
+
#=> #<FibonacciHeap n=0 min=nil>
|
67
|
+
```
|
68
|
+
|
69
|
+
Return a new, empty `FibonacciHeap::Heap` instance.
|
70
|
+
|
71
|
+
#### `FibonacciHeap::Heap#n`
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
heap = FibonacciHeap::Heap.new
|
75
|
+
heap.insert(FibonacciHeap::Node.new('foo'))
|
76
|
+
heap.n
|
77
|
+
#=> 1
|
78
|
+
heap.size
|
79
|
+
#=> 1
|
80
|
+
heap.length
|
81
|
+
#=> 1
|
82
|
+
```
|
83
|
+
|
84
|
+
Return the current number of nodes in the heap.
|
85
|
+
|
86
|
+
Aliased to `size` and `length`.
|
87
|
+
|
88
|
+
#### `FibonacciHeap::Heap#empty?`
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
heap = FibonacciHeap::Heap.new
|
92
|
+
heap.empty?
|
93
|
+
#=> true
|
94
|
+
```
|
95
|
+
|
96
|
+
Returns whether or not the heap is empty.
|
97
|
+
|
98
|
+
#### `FibonacciHeap::Heap#min`
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
heap = FibonacciHeap::Heap.new
|
102
|
+
heap.insert(FibonacciHeap::Node.new(1))
|
103
|
+
heap.insert(FibonacciHeap::Node.new(2))
|
104
|
+
heap.min
|
105
|
+
#=> #<FibonacciHeap::Node key=1 ...>
|
106
|
+
```
|
107
|
+
|
108
|
+
Return the smallest `FibonacciHeap::Node` node in the heap as determined by the node's `key`.
|
109
|
+
|
110
|
+
Will return `nil` if the heap is empty.
|
111
|
+
|
112
|
+
#### `FibonacciHeap::Heap#insert(x[, k])`
|
113
|
+
|
114
|
+
```ruby
|
115
|
+
heap = FibonacciHeap::Heap.new
|
116
|
+
node = FibonacciHeap::Node.new(1, 'foo')
|
117
|
+
node2 = FibonacciHeap::Node.new(0, 'bar')
|
118
|
+
heap.insert(node)
|
119
|
+
heap.insert(bar, 100)
|
120
|
+
```
|
121
|
+
|
122
|
+
Insert the given `FibonacciHeap::Node` `x` into the heap with an optional key `k`.
|
123
|
+
|
124
|
+
Defaults to using `x`'s existing `key` for `k`.
|
125
|
+
|
126
|
+
#### `FibonacciHeap::Heap#concat(h2)`
|
127
|
+
|
128
|
+
```ruby
|
129
|
+
heap = FibonacciHeap::Heap.new
|
130
|
+
heap.insert(FibonacciHeap::Node.new(1, 'foo'))
|
131
|
+
heap2 = FibonacciHeap::Heap.new
|
132
|
+
heap2.insert(FibonacciHeap::Node.new(2, 'bar'))
|
133
|
+
|
134
|
+
heap3 = heap.concat(heap2)
|
135
|
+
#=> #<FibonacciHeap::Heap n=2 min=#<FibonacciHeap::Node key=1 value="foo">>
|
136
|
+
|
137
|
+
heap3.pop
|
138
|
+
#=> #<FibonacciHeap::Node key=1 value="foo" ...>
|
139
|
+
heap3.pop
|
140
|
+
#=> #<FibonacciHeap::Node key=2 value="bar" ...>
|
141
|
+
```
|
142
|
+
|
143
|
+
Unite the given `FibonacciHeap::Heap` `h2` with this one in a new `FibonacciHeap::Heap`.
|
144
|
+
|
145
|
+
As this will mutate both collections of rooted trees, attempting to use either the original heap or `h2` after `concat` has undefined behaviour.
|
146
|
+
|
147
|
+
#### `FibonacciHeap::Heap#pop`
|
148
|
+
|
149
|
+
```ruby
|
150
|
+
heap = FibonacciHeap::Heap.new
|
151
|
+
heap.insert(FibonacciHeap::Node.new(1, 'foo'))
|
152
|
+
heap.pop
|
153
|
+
#=> #<FibonacciHeap::Node key=1 value="foo" ...>
|
154
|
+
```
|
155
|
+
|
156
|
+
Remove and return the smallest `FibonacciHeap::Node` from the heap.
|
157
|
+
|
158
|
+
#### `FibonacciHeap::Heap#decrease_key(x, k)`
|
159
|
+
|
160
|
+
```ruby
|
161
|
+
heap = FibonacciHeap::Heap.new
|
162
|
+
node = FibonacciHeap::Node.new(1, 'foo')
|
163
|
+
heap.insert(node)
|
164
|
+
heap.decrease_key(node, 0)
|
165
|
+
#=> #<FibonacciHeap::Node key=0 value="foo">
|
166
|
+
```
|
167
|
+
|
168
|
+
Decrease the key of the given `FibonacciHeap::Node` `x` to the new given key `k`.
|
169
|
+
|
170
|
+
The node must already be inserted into the heap and the key must be comparable.
|
171
|
+
|
172
|
+
Raises a `FibonacciHeap::InvalidKeyError` if the new key is greater than the current key.
|
173
|
+
|
174
|
+
#### `FibonacciHeap::Heap#delete(x)`
|
175
|
+
|
176
|
+
```ruby
|
177
|
+
heap = FibonacciHeap::Heap.new
|
178
|
+
node = FibonacciHeap::Node.new(1, 'foo')
|
179
|
+
heap.insert(node)
|
180
|
+
heap.delete(node)
|
181
|
+
#=> #<FibonacciHeap::Node key=-Infinity value="foo">
|
182
|
+
```
|
183
|
+
|
184
|
+
Deletes the given `FibonacciHeap::Node` `x` from the heap.
|
185
|
+
|
186
|
+
The node must already be inserted into the heap.
|
187
|
+
|
188
|
+
### `FibonacciHeap::Node`
|
189
|
+
|
190
|
+
A single node in a `FibonacciHeap::Heap`.
|
191
|
+
|
192
|
+
Used internally to form both min-heap ordered trees and circular, doubly linked lists.
|
193
|
+
|
194
|
+
#### `FibonacciHeap::Node.new(key[, value])`
|
195
|
+
|
196
|
+
```ruby
|
197
|
+
node = FibonacciHeap::Node.new(1)
|
198
|
+
#=> #<FibonacciHeap::Node key=1 value=1>
|
199
|
+
node = FibonacciHeap::Node.new(1, 'foo')
|
200
|
+
#=> #<FibonacciHeap::Node key=1 value="foo">
|
201
|
+
```
|
202
|
+
|
203
|
+
Return a new `FibonacciHeap::Node` with the given key `key` and an optional value `value`.
|
204
|
+
|
205
|
+
Defaults to using the `key` as the value.
|
206
|
+
|
207
|
+
#### `FibonacciHeap::Node#key`
|
208
|
+
|
209
|
+
```ruby
|
210
|
+
node = FibonacciHeap::Node.new(1, 'foo')
|
211
|
+
node.key
|
212
|
+
#=> 1
|
213
|
+
```
|
214
|
+
|
215
|
+
Return the current key of the node.
|
216
|
+
|
217
|
+
#### `FibonacciHeap::Node#value`
|
218
|
+
|
219
|
+
```ruby
|
220
|
+
node = FibonacciHeap::Node.new(1, 'foo')
|
221
|
+
node.value
|
222
|
+
#=> "foo"
|
223
|
+
```
|
224
|
+
|
225
|
+
Return the current value of the node.
|
226
|
+
|
227
|
+
### `FibonacciHeap::InvalidKeyError`
|
228
|
+
|
229
|
+
Raised when attempting to decrease a key but the key is greater than the current key.
|
230
|
+
|
231
|
+
## References
|
232
|
+
|
233
|
+
* Cormen, T. H., Leiserson, C. E., Rivest, R. L. & Stein, C., [Introduction to Algorithms, Third Edition](https://mitpress.mit.edu/books/introduction-algorithms-third-edition).
|
234
|
+
|
235
|
+
## License
|
236
|
+
|
237
|
+
Copyright © 2018 Paul Mucur
|
238
|
+
|
239
|
+
Distributed under the MIT License.
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module FibonacciHeap
|
2
|
+
# A Circular Doubly Linked List data structure.
|
3
|
+
#
|
4
|
+
# An unsorted, linear, circular list with a sentinel to simplify
|
5
|
+
# boundary conditions.
|
6
|
+
class CircularDoublyLinkedList
|
7
|
+
include Enumerable
|
8
|
+
|
9
|
+
# A Sentinel node to simplify boundary conditions in the linked list.
|
10
|
+
class Sentinel
|
11
|
+
attr_accessor :next, :prev
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@next = self
|
15
|
+
@prev = self
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Return the special "sentinel" or Nil node for this list.
|
20
|
+
attr_accessor :sentinel
|
21
|
+
|
22
|
+
# Return a new, empty list.
|
23
|
+
def initialize
|
24
|
+
@sentinel = Sentinel.new
|
25
|
+
end
|
26
|
+
|
27
|
+
# Return the first element of the list or nil if it is empty.
|
28
|
+
def head
|
29
|
+
return if empty?
|
30
|
+
|
31
|
+
sentinel.next
|
32
|
+
end
|
33
|
+
|
34
|
+
# Return the last element of the list or nil if it is empty.
|
35
|
+
def tail
|
36
|
+
return if empty?
|
37
|
+
|
38
|
+
sentinel.prev
|
39
|
+
end
|
40
|
+
|
41
|
+
# Return whether or not the list is empty.
|
42
|
+
def empty?
|
43
|
+
sentinel.next == sentinel
|
44
|
+
end
|
45
|
+
|
46
|
+
# Insert a given node into the list.
|
47
|
+
#
|
48
|
+
# New nodes will be placed at the head of the list.
|
49
|
+
def insert(x)
|
50
|
+
x.next = sentinel.next
|
51
|
+
sentinel.next.prev = x
|
52
|
+
sentinel.next = x
|
53
|
+
x.prev = sentinel
|
54
|
+
end
|
55
|
+
|
56
|
+
# Remove a given node from the list.
|
57
|
+
#
|
58
|
+
# The node must already be in the list.
|
59
|
+
def delete(x)
|
60
|
+
x.prev.next = x.next
|
61
|
+
x.next.prev = x.prev
|
62
|
+
end
|
63
|
+
|
64
|
+
# Combine this list with another, destroying the given list.
|
65
|
+
def concat(list)
|
66
|
+
list.sentinel.prev.next = sentinel.next
|
67
|
+
sentinel.next.prev = list.sentinel.prev
|
68
|
+
sentinel.next = list.sentinel.next
|
69
|
+
list.sentinel.prev = sentinel
|
70
|
+
end
|
71
|
+
|
72
|
+
# Yield each element of this list.
|
73
|
+
def each
|
74
|
+
x = sentinel.next
|
75
|
+
|
76
|
+
while x != sentinel
|
77
|
+
current = x
|
78
|
+
x = x.next
|
79
|
+
|
80
|
+
yield current
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,239 @@
|
|
1
|
+
require 'fibonacci_heap/circular_doubly_linked_list'
|
2
|
+
|
3
|
+
module FibonacciHeap
|
4
|
+
InvalidKeyError = Class.new(StandardError)
|
5
|
+
|
6
|
+
# A Fibonacci Heap data structure.
|
7
|
+
#
|
8
|
+
# A "mergeable heap" that supports several operations that run in
|
9
|
+
# constant amortized time. Structured as a collection of rooted trees
|
10
|
+
# that are min-heap ordered.
|
11
|
+
class Heap
|
12
|
+
# Return the current number of nodes in the heap.
|
13
|
+
attr_accessor :n
|
14
|
+
alias size n
|
15
|
+
alias length n
|
16
|
+
|
17
|
+
# Return the smallest node in the heap.
|
18
|
+
attr_accessor :min
|
19
|
+
|
20
|
+
# Return the root list of the heap.
|
21
|
+
attr_accessor :root_list
|
22
|
+
|
23
|
+
# Return a new, empty Fibonacci heap.
|
24
|
+
def initialize
|
25
|
+
@n = 0
|
26
|
+
@min = nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def empty?
|
30
|
+
n.zero?
|
31
|
+
end
|
32
|
+
|
33
|
+
# Insert a new node with an optional key into the heap.
|
34
|
+
#
|
35
|
+
# The node must be compatible with a FibonacciHeap::Node.
|
36
|
+
#
|
37
|
+
# Defaults to using the existing key of the node.
|
38
|
+
#
|
39
|
+
# Corresponds to the Fib-Heap-Insert(H, x) procedure.
|
40
|
+
def insert(x, k = x.key)
|
41
|
+
x.degree = 0
|
42
|
+
x.p = nil
|
43
|
+
x.child_list = CircularDoublyLinkedList.new
|
44
|
+
x.mark = false
|
45
|
+
x.key = k
|
46
|
+
|
47
|
+
if !min
|
48
|
+
# Create a root list for H containing just x
|
49
|
+
self.root_list = CircularDoublyLinkedList.new
|
50
|
+
root_list.insert(x)
|
51
|
+
|
52
|
+
self.min = x
|
53
|
+
else
|
54
|
+
# Insert x into H's root list
|
55
|
+
root_list.insert(x)
|
56
|
+
|
57
|
+
self.min = x if x.key < min.key
|
58
|
+
end
|
59
|
+
|
60
|
+
self.n += 1
|
61
|
+
|
62
|
+
x
|
63
|
+
end
|
64
|
+
|
65
|
+
# Unite the given Fibonacci heap into this one, returning a new heap.
|
66
|
+
#
|
67
|
+
# Note that uniting the heaps will mutate their root lists, destroying them
|
68
|
+
# both so attempting to use them has undefined behaviour.
|
69
|
+
#
|
70
|
+
# Corresponds to the Fib-Heap-Union(H1, H2) procedure.
|
71
|
+
def concat(h2)
|
72
|
+
h = self.class.new
|
73
|
+
h.min = min
|
74
|
+
|
75
|
+
h.root_list = root_list
|
76
|
+
h.root_list.concat(h2.root_list)
|
77
|
+
|
78
|
+
h.min = h2.min if !min || (h2.min && h2.min.key < min.key)
|
79
|
+
|
80
|
+
h.n = n + h2.n
|
81
|
+
|
82
|
+
h
|
83
|
+
end
|
84
|
+
|
85
|
+
# Remove and return the minimum node from the heap.
|
86
|
+
#
|
87
|
+
# After extracting the minimum node, the heap will consolidate its internal
|
88
|
+
# trees.
|
89
|
+
#
|
90
|
+
# Corresponds to the Fib-Heap-Extract-Min(H) procedure.
|
91
|
+
def pop
|
92
|
+
z = min
|
93
|
+
|
94
|
+
if z
|
95
|
+
# For each child x of z
|
96
|
+
z.child_list.each do |x|
|
97
|
+
|
98
|
+
# Add x to the root list of H
|
99
|
+
root_list.insert(x)
|
100
|
+
x.p = nil
|
101
|
+
end
|
102
|
+
|
103
|
+
# Remove z from the root list of H
|
104
|
+
root_list.delete(z)
|
105
|
+
|
106
|
+
# Is z the only node on the root list?
|
107
|
+
if z.right == z.left
|
108
|
+
# Empty the heap
|
109
|
+
self.min = nil
|
110
|
+
self.root_list = CircularDoublyLinkedList.new
|
111
|
+
else
|
112
|
+
# Set min to another root
|
113
|
+
self.min = root_list.head
|
114
|
+
|
115
|
+
consolidate
|
116
|
+
end
|
117
|
+
|
118
|
+
self.n -= 1
|
119
|
+
end
|
120
|
+
|
121
|
+
z
|
122
|
+
end
|
123
|
+
|
124
|
+
# Decrease the key of a given node to a new given key.
|
125
|
+
#
|
126
|
+
# The node must be compatible with a FibonacciHeap::Node and have been inserted
|
127
|
+
# in the heap already. The key must be comparable.
|
128
|
+
#
|
129
|
+
# Raises an InvalidKeyError if the new key is greater than the current key.
|
130
|
+
#
|
131
|
+
# Corresponds to the Fib-Heap-Decrease-Key(H, x, k) procedure.
|
132
|
+
def decrease_key(x, k)
|
133
|
+
raise InvalidKeyError, "new key #{k} is greater than current key #{x.key}" if k > x.key
|
134
|
+
|
135
|
+
x.key = k
|
136
|
+
y = x.p
|
137
|
+
|
138
|
+
if y && x.key < y.key
|
139
|
+
cut(x, y)
|
140
|
+
cascading_cut(y)
|
141
|
+
end
|
142
|
+
|
143
|
+
self.min = x if x.key < min.key
|
144
|
+
|
145
|
+
x
|
146
|
+
end
|
147
|
+
|
148
|
+
# Delete a node from the heap.
|
149
|
+
#
|
150
|
+
# The given node must be compatible with a FibonacciHeap::Node and have been
|
151
|
+
# inserted in the heap already.
|
152
|
+
#
|
153
|
+
# Corresponds to the Fib-Heap-Delete(H, x) procedure.
|
154
|
+
def delete(x)
|
155
|
+
decrease_key(x, -1.0 / 0.0)
|
156
|
+
pop
|
157
|
+
end
|
158
|
+
|
159
|
+
def inspect
|
160
|
+
%(#<#{self.class} n=#{n} min=#{min.inspect}>)
|
161
|
+
end
|
162
|
+
|
163
|
+
private
|
164
|
+
|
165
|
+
# Corresponds to the Consolidate(H) procedure.
|
166
|
+
def consolidate
|
167
|
+
degrees = []
|
168
|
+
|
169
|
+
root_list.each do |w|
|
170
|
+
x = w
|
171
|
+
d = x.degree
|
172
|
+
|
173
|
+
while degrees[d]
|
174
|
+
y = degrees[d]
|
175
|
+
|
176
|
+
x, y = y, x if x.key > y.key
|
177
|
+
|
178
|
+
link(y, x)
|
179
|
+
|
180
|
+
degrees[d] = nil
|
181
|
+
|
182
|
+
d += 1
|
183
|
+
end
|
184
|
+
|
185
|
+
degrees[d] = x
|
186
|
+
end
|
187
|
+
|
188
|
+
# Empty the root list
|
189
|
+
self.min = nil
|
190
|
+
self.root_list = CircularDoublyLinkedList.new
|
191
|
+
|
192
|
+
# Reconstruct the root list from the array A
|
193
|
+
degrees.each do |root|
|
194
|
+
next unless root
|
195
|
+
|
196
|
+
root_list.insert(root)
|
197
|
+
|
198
|
+
if !min
|
199
|
+
self.min = root
|
200
|
+
elsif root.key < min.key
|
201
|
+
self.min = root
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
# Corresponds to the Fib-Heap-Link(H, y, x) procedure.
|
207
|
+
def link(y, x)
|
208
|
+
# remove y from the root list of H
|
209
|
+
root_list.delete(y)
|
210
|
+
# make y a child of x, incrementing x.degree
|
211
|
+
x.child_list.insert(y)
|
212
|
+
x.degree += 1
|
213
|
+
y.p = x
|
214
|
+
y.mark = false
|
215
|
+
end
|
216
|
+
|
217
|
+
# Corresponds to the Cut(H, x, y) procedure.
|
218
|
+
def cut(x, y)
|
219
|
+
y.child_list.delete(x)
|
220
|
+
y.degree -= 1
|
221
|
+
root_list.insert(x)
|
222
|
+
x.p = nil
|
223
|
+
x.mark = false
|
224
|
+
end
|
225
|
+
|
226
|
+
# Corresponds to the Cascading-Cut(H, y) procedure.
|
227
|
+
def cascading_cut(y)
|
228
|
+
z = y.p
|
229
|
+
return unless z
|
230
|
+
|
231
|
+
if !y.mark
|
232
|
+
y.mark = true
|
233
|
+
else
|
234
|
+
cut(y, z)
|
235
|
+
cascading_cut(z)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'fibonacci_heap/circular_doubly_linked_list'
|
2
|
+
|
3
|
+
module FibonacciHeap
|
4
|
+
# A single node in a Fibonacci Heap.
|
5
|
+
#
|
6
|
+
# Contains a key and optional value that can be any arbitrary object.
|
7
|
+
# The key will be used to sort nodes so that the key of a node is greater
|
8
|
+
# than or equal to the key of its parent.
|
9
|
+
#
|
10
|
+
# Defaults to storing the key as the value.
|
11
|
+
class Node
|
12
|
+
# Return the key of the node.
|
13
|
+
attr_accessor :key
|
14
|
+
|
15
|
+
# Return the value of the node.
|
16
|
+
attr_accessor :value
|
17
|
+
|
18
|
+
# Return the node next to this one.
|
19
|
+
attr_accessor :next
|
20
|
+
alias right next
|
21
|
+
|
22
|
+
# Return the node previous to this one.
|
23
|
+
attr_accessor :prev
|
24
|
+
alias left prev
|
25
|
+
|
26
|
+
# Return the degree of this node.
|
27
|
+
attr_accessor :degree
|
28
|
+
|
29
|
+
# Return the parent of this node or nil if there is none.
|
30
|
+
attr_accessor :p
|
31
|
+
|
32
|
+
# Return whether this node is marked or not.
|
33
|
+
attr_accessor :mark
|
34
|
+
|
35
|
+
# Return the list of child nodes for this node.
|
36
|
+
attr_accessor :child_list
|
37
|
+
|
38
|
+
# Return a new node with the given key and optional value.
|
39
|
+
#
|
40
|
+
# The key and value can be any arbitrary object.
|
41
|
+
#
|
42
|
+
# The node's next and previous pointers will default to itself.
|
43
|
+
#
|
44
|
+
# Defaults the value to the key.
|
45
|
+
def initialize(key, value = key)
|
46
|
+
@key = key
|
47
|
+
@value = value
|
48
|
+
@next = self
|
49
|
+
@prev = self
|
50
|
+
end
|
51
|
+
|
52
|
+
def inspect
|
53
|
+
%(#<#{self.class} key=#{key.inspect} value=#{value.inspect}>)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,269 @@
|
|
1
|
+
require 'fibonacci_heap'
|
2
|
+
|
3
|
+
module FibonacciHeap
|
4
|
+
RSpec.describe CircularDoublyLinkedList do
|
5
|
+
describe '#insert' do
|
6
|
+
it 'inserts the node into an empty list' do
|
7
|
+
list = described_class.new
|
8
|
+
node = Node.new('foo')
|
9
|
+
|
10
|
+
list.insert(node)
|
11
|
+
|
12
|
+
expect(list).to contain_exactly(node)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'sets next and prev on the only node in a list' do
|
16
|
+
list = described_class.new
|
17
|
+
node = Node.new('foo')
|
18
|
+
|
19
|
+
list.insert(node)
|
20
|
+
|
21
|
+
expect(node.next).to eq(list.sentinel)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'sets the node as the head of an empty list' do
|
25
|
+
list = described_class.new
|
26
|
+
node = Node.new('foo')
|
27
|
+
|
28
|
+
list.insert(node)
|
29
|
+
|
30
|
+
expect(list.head).to eq(node)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'sets the node as the tail of an empty list' do
|
34
|
+
list = described_class.new
|
35
|
+
node = Node.new('foo')
|
36
|
+
|
37
|
+
list.insert(node)
|
38
|
+
|
39
|
+
expect(list.tail).to eq(node)
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'inserts the node into a non-empty list' do
|
43
|
+
list = described_class.new
|
44
|
+
node = Node.new('foo')
|
45
|
+
node2 = Node.new('bar')
|
46
|
+
|
47
|
+
list.insert(node)
|
48
|
+
list.insert(node2)
|
49
|
+
|
50
|
+
expect(list).to contain_exactly(node, node2)
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'sets the new node as the new head in a non-empty list' do
|
54
|
+
list = described_class.new
|
55
|
+
node = Node.new('foo')
|
56
|
+
node2 = Node.new('bar')
|
57
|
+
|
58
|
+
list.insert(node)
|
59
|
+
list.insert(node2)
|
60
|
+
|
61
|
+
expect(list.head).to eq(node2)
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'retains the existing tail in a non-empty list' do
|
65
|
+
list = described_class.new
|
66
|
+
node = Node.new('foo')
|
67
|
+
node2 = Node.new('bar')
|
68
|
+
|
69
|
+
list.insert(node)
|
70
|
+
list.insert(node2)
|
71
|
+
|
72
|
+
expect(list.tail).to eq(node)
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'inserts into a list with more than two nodes' do
|
76
|
+
list = described_class.new
|
77
|
+
node = Node.new('foo')
|
78
|
+
node2 = Node.new('bar')
|
79
|
+
node3 = Node.new('baz')
|
80
|
+
|
81
|
+
list.insert(node)
|
82
|
+
list.insert(node2)
|
83
|
+
list.insert(node3)
|
84
|
+
|
85
|
+
expect(list).to contain_exactly(node, node2, node3)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe '#delete' do
|
90
|
+
it 'removes the node from a list with only one node' do
|
91
|
+
list = described_class.new
|
92
|
+
node = Node.new('foo')
|
93
|
+
|
94
|
+
list.insert(node)
|
95
|
+
list.delete(node)
|
96
|
+
|
97
|
+
expect(list).to be_empty
|
98
|
+
expect(list.head).to be_nil
|
99
|
+
expect(list.tail).to be_nil
|
100
|
+
expect(list.to_a).to eq([])
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'removes a node from the tail of a non-empty list' do
|
104
|
+
list = described_class.new
|
105
|
+
node = Node.new('foo')
|
106
|
+
node2 = Node.new('bar')
|
107
|
+
|
108
|
+
list.insert(node)
|
109
|
+
list.insert(node2)
|
110
|
+
list.delete(node)
|
111
|
+
|
112
|
+
expect(list).to contain_exactly(node2)
|
113
|
+
expect(list.head).to eq(node2)
|
114
|
+
expect(list.tail).to eq(node2)
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'removes a node from the middle of a non-empty list' do
|
118
|
+
list = described_class.new
|
119
|
+
node = Node.new('foo')
|
120
|
+
node2 = Node.new('bar')
|
121
|
+
node3 = Node.new('baz')
|
122
|
+
|
123
|
+
list.insert(node)
|
124
|
+
list.insert(node2)
|
125
|
+
list.insert(node3)
|
126
|
+
list.delete(node2)
|
127
|
+
|
128
|
+
expect(list).to contain_exactly(node, node3)
|
129
|
+
expect(list.tail).to eq(node)
|
130
|
+
expect(list.head).to eq(node3)
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'can remove all nodes from a non-empty list' do
|
134
|
+
list = described_class.new
|
135
|
+
node = Node.new('foo')
|
136
|
+
node2 = Node.new('bar')
|
137
|
+
node3 = Node.new('baz')
|
138
|
+
|
139
|
+
list.insert(node)
|
140
|
+
list.insert(node2)
|
141
|
+
list.insert(node3)
|
142
|
+
list.delete(node2)
|
143
|
+
list.delete(node)
|
144
|
+
list.delete(node3)
|
145
|
+
|
146
|
+
expect(list).to be_empty
|
147
|
+
expect(list.head).to be_nil
|
148
|
+
expect(list.tail).to be_nil
|
149
|
+
expect(list.to_a).to eq([])
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'does not alter the pointers of the deleted node' do
|
153
|
+
list = described_class.new
|
154
|
+
node = Node.new('foo')
|
155
|
+
node2 = Node.new('bar')
|
156
|
+
node3 = Node.new('baz')
|
157
|
+
|
158
|
+
list.insert(node)
|
159
|
+
list.insert(node2)
|
160
|
+
list.insert(node3)
|
161
|
+
list.delete(node2)
|
162
|
+
|
163
|
+
expect(node2.next).to eq(node)
|
164
|
+
expect(node2.prev).to eq(node3)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
describe '#empty?' do
|
169
|
+
it 'is true if there is no head node' do
|
170
|
+
list = described_class.new
|
171
|
+
|
172
|
+
expect(list).to be_empty
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
describe '#each' do
|
177
|
+
it 'yields nothing if the list is empty' do
|
178
|
+
list = described_class.new
|
179
|
+
|
180
|
+
expect { |b| list.each(&b) }.to yield_successive_args
|
181
|
+
end
|
182
|
+
|
183
|
+
it 'yields nodes' do
|
184
|
+
list = described_class.new
|
185
|
+
node = Node.new('foo')
|
186
|
+
node2 = Node.new('bar')
|
187
|
+
list.insert(node)
|
188
|
+
list.insert(node2)
|
189
|
+
|
190
|
+
expect { |b| list.each(&b) }.to yield_successive_args(node2, node)
|
191
|
+
end
|
192
|
+
|
193
|
+
it 'yields more than two nodes' do
|
194
|
+
list = described_class.new
|
195
|
+
node = Node.new('foo')
|
196
|
+
node2 = Node.new('bar')
|
197
|
+
node3 = Node.new('baz')
|
198
|
+
list.insert(node)
|
199
|
+
list.insert(node2)
|
200
|
+
list.insert(node3)
|
201
|
+
|
202
|
+
expect { |b| list.each(&b) }.to yield_successive_args(node3, node2, node)
|
203
|
+
end
|
204
|
+
|
205
|
+
it 'is not affected by changes to the node while iterating' do
|
206
|
+
list = described_class.new
|
207
|
+
list2 = described_class.new
|
208
|
+
node = Node.new('foo')
|
209
|
+
node2 = Node.new('bar')
|
210
|
+
node3 = Node.new('baz')
|
211
|
+
list.insert(node)
|
212
|
+
list.insert(node2)
|
213
|
+
list2.insert(node3)
|
214
|
+
|
215
|
+
expect { |b|
|
216
|
+
list.each do |n|
|
217
|
+
list2.insert(n)
|
218
|
+
b.to_proc.call(n)
|
219
|
+
end
|
220
|
+
}.to yield_successive_args(node2, node)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
describe '#concat' do
|
225
|
+
it 'combines two lists' do
|
226
|
+
list = described_class.new
|
227
|
+
list2 = described_class.new
|
228
|
+
node = Node.new('foo')
|
229
|
+
node2 = Node.new('bar')
|
230
|
+
node3 = Node.new('baz')
|
231
|
+
node4 = Node.new('quux')
|
232
|
+
list.insert(node)
|
233
|
+
list.insert(node2)
|
234
|
+
list2.insert(node3)
|
235
|
+
list2.insert(node4)
|
236
|
+
|
237
|
+
list.concat(list2)
|
238
|
+
|
239
|
+
expect(list).to contain_exactly(node, node2, node3, node4)
|
240
|
+
end
|
241
|
+
|
242
|
+
it 'combines an empty list a non-empty one' do
|
243
|
+
list = described_class.new
|
244
|
+
list2 = described_class.new
|
245
|
+
node = Node.new('foo')
|
246
|
+
node2 = Node.new('bar')
|
247
|
+
list2.insert(node)
|
248
|
+
list2.insert(node2)
|
249
|
+
|
250
|
+
list.concat(list2)
|
251
|
+
|
252
|
+
expect(list).to contain_exactly(node, node2)
|
253
|
+
end
|
254
|
+
|
255
|
+
it 'combines a non-empty list with an empty one' do
|
256
|
+
list = described_class.new
|
257
|
+
list2 = described_class.new
|
258
|
+
node = Node.new('foo')
|
259
|
+
node2 = Node.new('bar')
|
260
|
+
list.insert(node)
|
261
|
+
list.insert(node2)
|
262
|
+
|
263
|
+
list.concat(list2)
|
264
|
+
|
265
|
+
expect(list).to contain_exactly(node, node2)
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
@@ -0,0 +1,311 @@
|
|
1
|
+
require 'fibonacci_heap'
|
2
|
+
|
3
|
+
module FibonacciHeap
|
4
|
+
RSpec.describe Heap do
|
5
|
+
it 'initializes n to 0' do
|
6
|
+
heap = described_class.new
|
7
|
+
|
8
|
+
expect(heap.n).to be_zero
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'initializes min to nil' do
|
12
|
+
heap = described_class.new
|
13
|
+
|
14
|
+
expect(heap.min).to be_nil
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#inspect' do
|
18
|
+
it 'lists the size and min of the heap' do
|
19
|
+
heap = described_class.new
|
20
|
+
heap.insert(Node.new(1, 'foo'))
|
21
|
+
|
22
|
+
expect(heap.inspect).to eq('#<FibonacciHeap::Heap n=1 min=#<FibonacciHeap::Node key=1 value="foo">>')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#n' do
|
27
|
+
it 'returns the size of the heap' do
|
28
|
+
heap = described_class.new
|
29
|
+
node = Node.new('foo')
|
30
|
+
node2 = Node.new('foo')
|
31
|
+
|
32
|
+
heap.insert(node)
|
33
|
+
heap.insert(node2)
|
34
|
+
|
35
|
+
expect(heap.n).to eq(2)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'is aliased to #size' do
|
39
|
+
heap = described_class.new
|
40
|
+
node = Node.new('foo')
|
41
|
+
node2 = Node.new('foo')
|
42
|
+
|
43
|
+
heap.insert(node)
|
44
|
+
heap.insert(node2)
|
45
|
+
|
46
|
+
expect(heap.size).to eq(2)
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'is aliased to #length' do
|
50
|
+
heap = described_class.new
|
51
|
+
node = Node.new('foo')
|
52
|
+
node2 = Node.new('foo')
|
53
|
+
|
54
|
+
heap.insert(node)
|
55
|
+
heap.insert(node2)
|
56
|
+
|
57
|
+
expect(heap.length).to eq(2)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe '#empty?' do
|
62
|
+
it 'returns true if the heap has no nodes' do
|
63
|
+
heap = described_class.new
|
64
|
+
|
65
|
+
expect(heap).to be_empty
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'returns false if the heap has any nodes' do
|
69
|
+
heap = described_class.new
|
70
|
+
heap.insert(Node.new('foo'))
|
71
|
+
|
72
|
+
expect(heap).to_not be_empty
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe '#min' do
|
77
|
+
it 'returns the smallest node in the heap' do
|
78
|
+
heap = described_class.new
|
79
|
+
node = Node.new(10)
|
80
|
+
node2 = Node.new(0)
|
81
|
+
|
82
|
+
heap.insert(node)
|
83
|
+
heap.insert(node2)
|
84
|
+
|
85
|
+
expect(heap.min).to eq(node2)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe '#insert' do
|
90
|
+
it 'adds the node to the root list' do
|
91
|
+
heap = described_class.new
|
92
|
+
node = Node.new('foo')
|
93
|
+
|
94
|
+
heap.insert(node)
|
95
|
+
|
96
|
+
expect(heap.root_list).to include(node)
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'sets the node to min for an empty heap' do
|
100
|
+
heap = described_class.new
|
101
|
+
node = Node.new('foo')
|
102
|
+
|
103
|
+
heap.insert(node)
|
104
|
+
|
105
|
+
expect(heap.min).to eq(node)
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'does not override min if a smaller node already exists' do
|
109
|
+
heap = described_class.new
|
110
|
+
node = Node.new('foo')
|
111
|
+
node2 = Node.new('bar')
|
112
|
+
|
113
|
+
heap.insert(node2)
|
114
|
+
heap.insert(node)
|
115
|
+
|
116
|
+
expect(heap.min).to eq(node2)
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'overrides min if the new node is smallest' do
|
120
|
+
heap = described_class.new
|
121
|
+
node = Node.new('foo')
|
122
|
+
node2 = Node.new('bar')
|
123
|
+
|
124
|
+
heap.insert(node)
|
125
|
+
heap.insert(node2)
|
126
|
+
|
127
|
+
expect(heap.min).to eq(node2)
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'increases the number of nodes in the heap' do
|
131
|
+
heap = described_class.new
|
132
|
+
node = Node.new('foo')
|
133
|
+
|
134
|
+
heap.insert(node)
|
135
|
+
|
136
|
+
expect(heap.n).to eq(1)
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'returns the inserted node' do
|
140
|
+
heap = described_class.new
|
141
|
+
node = Node.new('foo')
|
142
|
+
|
143
|
+
expect(heap.insert(node)).to eq(node)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
describe '#pop' do
|
148
|
+
it 'returns nil if the heap is empty' do
|
149
|
+
heap = described_class.new
|
150
|
+
|
151
|
+
expect(heap.pop).to be_nil
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'returns the smallest node' do
|
155
|
+
heap = described_class.new
|
156
|
+
node = Node.new(1)
|
157
|
+
node2 = Node.new(2)
|
158
|
+
|
159
|
+
heap.insert(node)
|
160
|
+
heap.insert(node2)
|
161
|
+
|
162
|
+
expect(heap.pop).to eq(node)
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'works with more than two nodes' do
|
166
|
+
heap = described_class.new
|
167
|
+
node = Node.new(-1)
|
168
|
+
heap.insert(node)
|
169
|
+
|
170
|
+
25.times do |i|
|
171
|
+
heap.insert(Node.new(i))
|
172
|
+
end
|
173
|
+
|
174
|
+
expect(heap.pop).to eq(node)
|
175
|
+
end
|
176
|
+
|
177
|
+
it 'decreases the size of the heap' do
|
178
|
+
heap = described_class.new
|
179
|
+
node = Node.new(1)
|
180
|
+
node2 = Node.new(2)
|
181
|
+
|
182
|
+
heap.insert(node)
|
183
|
+
heap.insert(node2)
|
184
|
+
heap.pop
|
185
|
+
|
186
|
+
expect(heap.n).to eq(1)
|
187
|
+
end
|
188
|
+
|
189
|
+
it 'works when the min is in the middle of a tree' do
|
190
|
+
heap = described_class.new
|
191
|
+
node = Node.new(4)
|
192
|
+
node2 = Node.new(2)
|
193
|
+
node3 = Node.new(1)
|
194
|
+
node4 = Node.new(3)
|
195
|
+
|
196
|
+
heap.insert(node)
|
197
|
+
heap.insert(node2)
|
198
|
+
heap.insert(node3)
|
199
|
+
heap.insert(node4)
|
200
|
+
|
201
|
+
expect(heap.pop).to eq(node3)
|
202
|
+
expect(heap.pop).to eq(node2)
|
203
|
+
expect(heap.pop).to eq(node4)
|
204
|
+
expect(heap.pop).to eq(node)
|
205
|
+
end
|
206
|
+
|
207
|
+
it 'distinguishes a single root by linking together left and right' do
|
208
|
+
heap = described_class.new
|
209
|
+
root = Node.new('root')
|
210
|
+
|
211
|
+
heap.insert(root)
|
212
|
+
|
213
|
+
expect(root.left).to eq(root.right)
|
214
|
+
end
|
215
|
+
|
216
|
+
it 'does not link together left and right for multiple roots' do
|
217
|
+
heap = described_class.new
|
218
|
+
root = Node.new('foo')
|
219
|
+
root2 = Node.new('foo')
|
220
|
+
|
221
|
+
heap.insert(root)
|
222
|
+
heap.insert(root2)
|
223
|
+
|
224
|
+
expect(root.left).not_to eq(root.right)
|
225
|
+
expect(root2.left).not_to eq(root2.right)
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
describe '#decrease_key' do
|
230
|
+
it 'raises an error if the key is greater than the existing value' do
|
231
|
+
heap = described_class.new
|
232
|
+
node = Node.new(1)
|
233
|
+
heap.insert(node)
|
234
|
+
|
235
|
+
expect { heap.decrease_key(node, 4) }.to raise_error(InvalidKeyError)
|
236
|
+
end
|
237
|
+
|
238
|
+
it 'decreases the key of the node' do
|
239
|
+
heap = described_class.new
|
240
|
+
node = Node.new(2)
|
241
|
+
node2 = Node.new(3)
|
242
|
+
heap.insert(node)
|
243
|
+
heap.insert(node2)
|
244
|
+
|
245
|
+
heap.decrease_key(node2, 1)
|
246
|
+
|
247
|
+
expect(node2.key).to eq(1)
|
248
|
+
end
|
249
|
+
|
250
|
+
it 'updates the min of the heap' do
|
251
|
+
heap = described_class.new
|
252
|
+
node = Node.new(2)
|
253
|
+
node2 = Node.new(3)
|
254
|
+
heap.insert(node)
|
255
|
+
heap.insert(node2)
|
256
|
+
|
257
|
+
heap.decrease_key(node2, 1)
|
258
|
+
|
259
|
+
expect(heap.min).to eq(node2)
|
260
|
+
end
|
261
|
+
|
262
|
+
it 'returns the decreased node' do
|
263
|
+
heap = described_class.new
|
264
|
+
node = Node.new(2)
|
265
|
+
heap.insert(node)
|
266
|
+
|
267
|
+
expect(heap.decrease_key(node, 1)).to eq(node)
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
describe '#delete' do
|
272
|
+
it 'removes the node from the heap' do
|
273
|
+
heap = described_class.new
|
274
|
+
node = Node.new(2)
|
275
|
+
node2 = Node.new(1)
|
276
|
+
heap.insert(node)
|
277
|
+
heap.insert(node2)
|
278
|
+
|
279
|
+
heap.delete(node)
|
280
|
+
|
281
|
+
expect(heap.pop).to eq(node2)
|
282
|
+
expect(heap.pop).to be_nil
|
283
|
+
end
|
284
|
+
|
285
|
+
it 'returns the deleted node' do
|
286
|
+
heap = described_class.new
|
287
|
+
node = Node.new(1)
|
288
|
+
heap.insert(node)
|
289
|
+
|
290
|
+
expect(heap.delete(node)).to eq(node)
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
describe '#concat' do
|
295
|
+
it 'unite the given heap with this one' do
|
296
|
+
heap = described_class.new
|
297
|
+
node = Node.new(1)
|
298
|
+
heap2 = described_class.new
|
299
|
+
node2 = Node.new(2)
|
300
|
+
heap.insert(node)
|
301
|
+
heap2.insert(node2)
|
302
|
+
|
303
|
+
heap3 = heap.concat(heap2)
|
304
|
+
|
305
|
+
expect(heap3.pop).to eq(node)
|
306
|
+
expect(heap3.pop).to eq(node2)
|
307
|
+
expect(heap3.pop).to be_nil
|
308
|
+
end
|
309
|
+
end
|
310
|
+
end
|
311
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'fibonacci_heap/node'
|
2
|
+
|
3
|
+
module FibonacciHeap
|
4
|
+
RSpec.describe Node do
|
5
|
+
describe '#next' do
|
6
|
+
it 'is initialized to itself by default' do
|
7
|
+
node = described_class.new('foo')
|
8
|
+
|
9
|
+
expect(node.next).to eq(node)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe '#prev' do
|
14
|
+
it 'is initialized to itself by default' do
|
15
|
+
node = described_class.new('foo')
|
16
|
+
|
17
|
+
expect(node.prev).to eq(node)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#inspect' do
|
22
|
+
it 'lists the current key and value' do
|
23
|
+
node = described_class.new(1, 'foo')
|
24
|
+
|
25
|
+
expect(node.inspect).to eq('#<FibonacciHeap::Node key=1 value="foo">')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
RSpec.configure do |config|
|
2
|
+
config.expect_with :rspec do |expectations|
|
3
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
4
|
+
end
|
5
|
+
|
6
|
+
config.mock_with :rspec do |mocks|
|
7
|
+
mocks.verify_partial_doubles = true
|
8
|
+
end
|
9
|
+
|
10
|
+
config.shared_context_metadata_behavior = :apply_to_host_groups
|
11
|
+
config.filter_run_when_matching :focus
|
12
|
+
config.example_status_persistence_file_path = 'spec/examples.txt'
|
13
|
+
config.disable_monkey_patching!
|
14
|
+
config.warnings = true
|
15
|
+
config.default_formatter = 'doc' if config.files_to_run.one?
|
16
|
+
config.profile_examples = 10
|
17
|
+
config.order = :random
|
18
|
+
Kernel.srand config.seed
|
19
|
+
end
|
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fibonacci_heap
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Paul Mucur
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-07-13 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.7'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.7'
|
27
|
+
description: |2
|
28
|
+
A Ruby implementation of the Fibonacci heap data structure ideal for use as
|
29
|
+
a priority queue with Dijkstra's algorithm.
|
30
|
+
email:
|
31
|
+
executables: []
|
32
|
+
extensions: []
|
33
|
+
extra_rdoc_files: []
|
34
|
+
files:
|
35
|
+
- LICENSE
|
36
|
+
- README.md
|
37
|
+
- lib/fibonacci_heap.rb
|
38
|
+
- lib/fibonacci_heap/circular_doubly_linked_list.rb
|
39
|
+
- lib/fibonacci_heap/heap.rb
|
40
|
+
- lib/fibonacci_heap/node.rb
|
41
|
+
- spec/fibonacci_heap/circular_doubly_linked_list_spec.rb
|
42
|
+
- spec/fibonacci_heap/heap_spec.rb
|
43
|
+
- spec/fibonacci_heap/node_spec.rb
|
44
|
+
- spec/spec_helper.rb
|
45
|
+
homepage: https://github.com/mudge/fibonacci_heap
|
46
|
+
licenses:
|
47
|
+
- MIT
|
48
|
+
metadata: {}
|
49
|
+
post_install_message:
|
50
|
+
rdoc_options: []
|
51
|
+
require_paths:
|
52
|
+
- lib
|
53
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
requirements: []
|
64
|
+
rubyforge_project:
|
65
|
+
rubygems_version: 2.7.3
|
66
|
+
signing_key:
|
67
|
+
specification_version: 4
|
68
|
+
summary: A Ruby implementation of the Fibonacci heap data structure
|
69
|
+
test_files:
|
70
|
+
- spec/spec_helper.rb
|
71
|
+
- spec/fibonacci_heap/node_spec.rb
|
72
|
+
- spec/fibonacci_heap/heap_spec.rb
|
73
|
+
- spec/fibonacci_heap/circular_doubly_linked_list_spec.rb
|