pairing_heap 0.1.0 → 1.0.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 +4 -4
- data/.github/workflows/main.yml +11 -8
- data/Gemfile.lock +5 -5
- data/README.md +662 -106
- data/lib/pairing_heap/version.rb +1 -1
- data/lib/pairing_heap.rb +215 -64
- data/pairing_heap.gemspec +5 -4
- metadata +6 -5
data/lib/pairing_heap/version.rb
CHANGED
data/lib/pairing_heap.rb
CHANGED
@@ -1,18 +1,71 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module PairingHeap
|
4
|
+
module MergePairs
|
5
|
+
# Non-recursive implementation of method described in https://en.wikipedia.org/wiki/Pairing_heap#delete-min
|
6
|
+
def merge_pairs(heaps)
|
7
|
+
return nil if heaps.nil?
|
8
|
+
return heaps if heaps.next_sibling.nil?
|
9
|
+
|
10
|
+
# [H1, H2, H3, H4, H5, H6, H7] => [H1H2, H3H4, H5H6, H7]
|
11
|
+
pairs = nil
|
12
|
+
left = heaps
|
13
|
+
while left
|
14
|
+
right = left.next_sibling
|
15
|
+
unless right
|
16
|
+
left.next_sibling = pairs
|
17
|
+
pairs = left
|
18
|
+
break
|
19
|
+
end
|
20
|
+
next_val = right.next_sibling
|
21
|
+
right = meld(left, right)
|
22
|
+
right.next_sibling = pairs
|
23
|
+
pairs = right
|
24
|
+
|
25
|
+
left = next_val
|
26
|
+
end
|
27
|
+
|
28
|
+
# [H1H2, H3H4, H5H6, H7]
|
29
|
+
# [H1H2, H3H4, H5H67]
|
30
|
+
# [H1H2, H3H45H67]
|
31
|
+
# [H1H2H3H45H67]
|
32
|
+
# return H1H2H3H45H67
|
33
|
+
left = pairs
|
34
|
+
right = pairs.next_sibling
|
35
|
+
while right
|
36
|
+
next_val = right.next_sibling
|
37
|
+
left = meld(left, right)
|
38
|
+
right = next_val
|
39
|
+
end
|
40
|
+
left
|
41
|
+
end
|
42
|
+
end
|
43
|
+
private_constant :MergePairs
|
44
|
+
|
4
45
|
# Pairing heap data structure implementation
|
5
46
|
# @see https://en.wikipedia.org/wiki/Pairing_heap
|
6
47
|
class PairingHeap
|
7
48
|
class Node
|
8
49
|
attr_accessor :elem, :priority, :subheaps, :parent, :prev_sibling, :next_sibling
|
9
|
-
def initialize(elem, priority
|
50
|
+
def initialize(elem, priority)
|
10
51
|
@elem = elem
|
11
52
|
@priority = priority
|
12
|
-
@subheaps =
|
13
|
-
@parent =
|
14
|
-
@prev_sibling =
|
15
|
-
@next_sibling =
|
53
|
+
@subheaps = nil
|
54
|
+
@parent = nil
|
55
|
+
@prev_sibling = nil
|
56
|
+
@next_sibling = nil
|
57
|
+
end
|
58
|
+
|
59
|
+
def remove_from_parents_list!
|
60
|
+
if self.prev_sibling
|
61
|
+
self.prev_sibling.next_sibling = self.next_sibling
|
62
|
+
self.next_sibling.prev_sibling = self.prev_sibling if self.next_sibling
|
63
|
+
elsif self.parent.subheaps.equal?(self)
|
64
|
+
self.parent.subheaps = self.next_sibling
|
65
|
+
self.next_sibling.prev_sibling = nil if self.next_sibling
|
66
|
+
end
|
67
|
+
self.prev_sibling = nil
|
68
|
+
self.next_sibling = nil
|
16
69
|
end
|
17
70
|
end
|
18
71
|
private_constant :Node
|
@@ -33,12 +86,17 @@ module PairingHeap
|
|
33
86
|
def push(elem, priority)
|
34
87
|
raise ArgumentError, "Element already in the heap" if @nodes.key?(elem)
|
35
88
|
|
36
|
-
node = Node.new(elem, priority
|
89
|
+
node = Node.new(elem, priority)
|
37
90
|
@nodes[elem] = node
|
38
|
-
|
91
|
+
if @root
|
92
|
+
@root = meld(@root, node)
|
93
|
+
else
|
94
|
+
@root = node
|
95
|
+
end
|
39
96
|
self
|
40
97
|
end
|
41
98
|
alias enqueue push
|
99
|
+
alias offer push
|
42
100
|
|
43
101
|
# Returns the element at the top of the heap
|
44
102
|
# Time Complexity: O(1)
|
@@ -46,6 +104,14 @@ module PairingHeap
|
|
46
104
|
@root&.elem
|
47
105
|
end
|
48
106
|
|
107
|
+
def peek_priority
|
108
|
+
@root&.priority
|
109
|
+
end
|
110
|
+
|
111
|
+
def peek_with_priority
|
112
|
+
[@root&.elem, @root&.priority]
|
113
|
+
end
|
114
|
+
|
49
115
|
# Time Complexity: O(1)
|
50
116
|
# @return [Boolean]
|
51
117
|
def empty?
|
@@ -65,11 +131,10 @@ module PairingHeap
|
|
65
131
|
end
|
66
132
|
alias length size
|
67
133
|
|
68
|
-
# Removes element from the top of the heap
|
134
|
+
# Removes element from the top of the heap and returns it
|
69
135
|
# Time Complexity: O(N)
|
70
136
|
# Amortized time Complexity: O(log(N))
|
71
|
-
# @raise [
|
72
|
-
# @return [PairingHeap]
|
137
|
+
# @raise [ArgumentError] if the heap is empty
|
73
138
|
def pop
|
74
139
|
raise ArgumentError, "Cannot remove from an empty heap" if @root.nil?
|
75
140
|
|
@@ -85,12 +150,18 @@ module PairingHeap
|
|
85
150
|
end
|
86
151
|
alias dequeue pop
|
87
152
|
|
153
|
+
def pop_priority
|
154
|
+
node = @root
|
155
|
+
pop
|
156
|
+
[node.elem, node.priority]
|
157
|
+
end
|
158
|
+
|
88
159
|
# Changes a priority of element to a more prioritary one
|
89
160
|
# Time Complexity: O(1)
|
90
161
|
# Amortized Time Complexity: o(log(N))
|
91
162
|
# @param elem Element
|
92
163
|
# @param priority New priority
|
93
|
-
# @raise [ArgumentError] if the element
|
164
|
+
# @raise [ArgumentError] if the element is not in the heap or the new priority is less prioritary
|
94
165
|
# @return [PairingHeap]
|
95
166
|
def change_priority(elem, priority)
|
96
167
|
node = @nodes[elem]
|
@@ -103,16 +174,16 @@ module PairingHeap
|
|
103
174
|
return if node.parent.nil?
|
104
175
|
return if @order[node.parent.priority, node.priority]
|
105
176
|
|
106
|
-
remove_from_parents_list
|
177
|
+
node.remove_from_parents_list!
|
107
178
|
@root = meld(node, @root)
|
108
179
|
@root.parent = nil
|
109
180
|
self
|
110
181
|
end
|
111
182
|
|
112
|
-
# Removes element from the
|
183
|
+
# Removes element from the heap
|
113
184
|
# Time Complexity: O(N)
|
114
185
|
# Amortized Time Complexity: O(log(N))
|
115
|
-
# @raise [ArgumentError] if the element
|
186
|
+
# @raise [ArgumentError] if the element is not in the heap
|
116
187
|
# @return [PairingHeap]
|
117
188
|
def delete(elem)
|
118
189
|
node = @nodes[elem]
|
@@ -121,39 +192,37 @@ module PairingHeap
|
|
121
192
|
@nodes.delete(elem)
|
122
193
|
if node.parent.nil?
|
123
194
|
@root = merge_pairs(node.subheaps)
|
195
|
+
if @root
|
196
|
+
@root.parent = nil
|
197
|
+
@root.prev_sibling = nil
|
198
|
+
@root.next_sibling = nil
|
199
|
+
end
|
124
200
|
else
|
125
|
-
remove_from_parents_list
|
201
|
+
node.remove_from_parents_list!
|
126
202
|
new_heap = merge_pairs(node.subheaps)
|
127
203
|
if new_heap
|
128
|
-
|
129
|
-
|
204
|
+
@root = meld(new_heap, @root)
|
205
|
+
@root.parent = nil
|
206
|
+
@root.prev_sibling = nil
|
207
|
+
@root.next_sibling = nil
|
130
208
|
end
|
131
|
-
@root = meld(new_heap, @root)
|
132
209
|
end
|
133
|
-
@root&.parent = nil
|
134
210
|
self
|
135
211
|
end
|
136
212
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
node.parent.subheaps = node.next_sibling
|
145
|
-
node.next_sibling.prev_sibling = nil if node.next_sibling
|
146
|
-
elsif node.next_sibling
|
147
|
-
node.next_sibling.prev_sibling = nil
|
148
|
-
end
|
149
|
-
node.prev_sibling = nil
|
150
|
-
node.next_sibling = nil
|
213
|
+
# Returns priority of the provided element
|
214
|
+
# Time Complexity: O(1)
|
215
|
+
# @raise [ArgumentError] if the element is not in the heap
|
216
|
+
def get_priority(elem)
|
217
|
+
node = @nodes[elem]
|
218
|
+
raise ArgumentError, "Provided element is not in heap" if node.nil?
|
219
|
+
node.priority
|
151
220
|
end
|
152
221
|
|
153
|
-
|
154
|
-
|
155
|
-
return left if right.nil?
|
222
|
+
private
|
223
|
+
include MergePairs
|
156
224
|
|
225
|
+
def meld(left, right)
|
157
226
|
if @order[left.priority, right.priority]
|
158
227
|
parent = left
|
159
228
|
child = right
|
@@ -168,42 +237,124 @@ module PairingHeap
|
|
168
237
|
child.parent = parent
|
169
238
|
parent
|
170
239
|
end
|
240
|
+
end
|
171
241
|
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
242
|
+
class SimplePairingHeap
|
243
|
+
class Node
|
244
|
+
attr_accessor :elem, :priority, :subheaps, :next_sibling
|
245
|
+
def initialize(elem, priority)
|
246
|
+
@elem = elem
|
247
|
+
@priority = priority
|
248
|
+
@subheaps = nil
|
249
|
+
@next_sibling = nil
|
250
|
+
end
|
251
|
+
end
|
252
|
+
private_constant :Node
|
176
253
|
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
254
|
+
# @param &block Optional heap property priority comparator. `<:=.to_proc` by default
|
255
|
+
def initialize(&block)
|
256
|
+
@root = nil
|
257
|
+
@order = block || :<=.to_proc
|
258
|
+
@size = 0
|
259
|
+
end
|
260
|
+
|
261
|
+
# Pushes element to the heap.
|
262
|
+
# Time Complexity: O(1)
|
263
|
+
# @param elem Element to be pushed
|
264
|
+
# @param priority Priority of the element
|
265
|
+
# @return [PairingHeap]
|
266
|
+
def push(elem, priority)
|
267
|
+
node = Node.new(elem, priority)
|
268
|
+
if @root
|
269
|
+
@root = meld(@root, node)
|
270
|
+
else
|
271
|
+
@root = node
|
190
272
|
end
|
273
|
+
@size += 1
|
274
|
+
self
|
275
|
+
end
|
276
|
+
alias enqueue push
|
277
|
+
alias offer push
|
191
278
|
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
279
|
+
# Returns the element at the top of the heap
|
280
|
+
# Time Complexity: O(1)
|
281
|
+
def peek
|
282
|
+
@root&.elem
|
283
|
+
end
|
284
|
+
|
285
|
+
def peek_priority
|
286
|
+
@root&.priority
|
287
|
+
end
|
288
|
+
|
289
|
+
def peek_with_priority
|
290
|
+
[@root&.elem, @root&.priority]
|
291
|
+
end
|
292
|
+
|
293
|
+
# Time Complexity: O(1)
|
294
|
+
# @return [Boolean]
|
295
|
+
def empty?
|
296
|
+
@root.nil?
|
297
|
+
end
|
298
|
+
|
299
|
+
# Time Complexity: O(1)
|
300
|
+
# @return [Boolean]
|
301
|
+
def any?
|
302
|
+
!@root.nil?
|
303
|
+
end
|
304
|
+
|
305
|
+
# Time Complexity: O(1)
|
306
|
+
# @return [Integer]
|
307
|
+
def size
|
308
|
+
@size
|
309
|
+
end
|
310
|
+
alias length size
|
311
|
+
|
312
|
+
# Removes element from the top of the heap and returns it
|
313
|
+
# Time Complexity: O(N)
|
314
|
+
# Amortized time Complexity: O(log(N))
|
315
|
+
# @raise [ArgumEntError] if the heap is empty
|
316
|
+
def pop
|
317
|
+
raise ArgumentError, "Cannot remove from an empty heap" if @root.nil?
|
318
|
+
@size -= 1
|
319
|
+
|
320
|
+
elem = @root.elem
|
321
|
+
@root = merge_pairs(@root.subheaps)
|
322
|
+
@root&.next_sibling = nil
|
323
|
+
|
324
|
+
elem
|
325
|
+
end
|
326
|
+
alias dequeue pop
|
200
327
|
|
201
|
-
|
202
|
-
|
328
|
+
def pop_priority
|
329
|
+
node = @root
|
330
|
+
pop
|
331
|
+
node.priority
|
332
|
+
end
|
333
|
+
|
334
|
+
def pop_with_priority
|
335
|
+
node = @root
|
336
|
+
pop
|
337
|
+
[node.elem, node.priority]
|
338
|
+
end
|
339
|
+
|
340
|
+
private
|
341
|
+
include MergePairs
|
342
|
+
|
343
|
+
def meld(left, right)
|
344
|
+
if @order[left.priority, right.priority]
|
345
|
+
parent = left
|
346
|
+
child = right
|
347
|
+
else
|
348
|
+
parent = right
|
349
|
+
child = left
|
203
350
|
end
|
351
|
+
child.next_sibling = parent.subheaps
|
352
|
+
parent.subheaps = child
|
353
|
+
parent
|
204
354
|
end
|
205
355
|
end
|
206
356
|
|
357
|
+
|
207
358
|
# Priority queue where the smallest priority is the most prioritary
|
208
359
|
class MinPriorityQueue < PairingHeap
|
209
360
|
def initialize
|
@@ -231,7 +382,7 @@ module PairingHeap
|
|
231
382
|
# Changes a priority of the element to a more prioritary one
|
232
383
|
# Time Complexity: O(N)
|
233
384
|
# Amortized Time Complexity: O(log(N))
|
234
|
-
# @raise [ArgumentError] if the element
|
385
|
+
# @raise [ArgumentError] if the element is not in the heap
|
235
386
|
# @return [PairingHeap]
|
236
387
|
def change_priority(elem, priority)
|
237
388
|
raise ArgumentError, "Provided element is not in heap" unless @nodes.key?(elem)
|
data/pairing_heap.gemspec
CHANGED
@@ -8,14 +8,15 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.authors = ["Marcin Henryk Bartkowiak"]
|
9
9
|
spec.email = ["mhbartkowiak@gmail.com"]
|
10
10
|
|
11
|
-
spec.summary = "Performant priority queue in pure
|
12
|
-
spec.description = "Performant priority queue in pure
|
11
|
+
spec.summary = "Performant priority queue in pure ruby with support for changing priority"
|
12
|
+
spec.description = "Performant priority queue in pure ruby with support for changing priority using pairing heap data structure"
|
13
13
|
spec.homepage = "https://github.com/mhib/pairing_heap"
|
14
14
|
spec.license = "MIT"
|
15
15
|
spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
|
16
16
|
|
17
|
-
spec.metadata["homepage_uri"]
|
18
|
-
spec.metadata["source_code_uri"]
|
17
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
18
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
19
|
+
spec.metadata["documentation_uri"] = "https://rubydoc.info/gems/pairing_heap"
|
19
20
|
|
20
21
|
# Specify which files should be added to the gem when it is released.
|
21
22
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pairing_heap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Marcin Henryk Bartkowiak
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-09-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
@@ -38,7 +38,7 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '13.0'
|
41
|
-
description: Performant priority queue in pure
|
41
|
+
description: Performant priority queue in pure ruby with support for changing priority
|
42
42
|
using pairing heap data structure
|
43
43
|
email:
|
44
44
|
- mhbartkowiak@gmail.com
|
@@ -65,6 +65,7 @@ licenses:
|
|
65
65
|
metadata:
|
66
66
|
homepage_uri: https://github.com/mhib/pairing_heap
|
67
67
|
source_code_uri: https://github.com/mhib/pairing_heap
|
68
|
+
documentation_uri: https://rubydoc.info/gems/pairing_heap
|
68
69
|
post_install_message:
|
69
70
|
rdoc_options: []
|
70
71
|
require_paths:
|
@@ -80,8 +81,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
80
81
|
- !ruby/object:Gem::Version
|
81
82
|
version: '0'
|
82
83
|
requirements: []
|
83
|
-
rubygems_version: 3.
|
84
|
+
rubygems_version: 3.3.3
|
84
85
|
signing_key:
|
85
86
|
specification_version: 4
|
86
|
-
summary: Performant priority queue in pure
|
87
|
+
summary: Performant priority queue in pure ruby with support for changing priority
|
87
88
|
test_files: []
|