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.
@@ -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.
@@ -0,0 +1,239 @@
1
+ # Fibonacci Heap [![Build Status](https://travis-ci.org/mudge/fibonacci_heap.svg?branch=master)](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,2 @@
1
+ require 'fibonacci_heap/heap'
2
+ require 'fibonacci_heap/node'
@@ -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
@@ -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