pairing_heap 0.1.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|