fibonacci_heap 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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