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.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PairingHeap
4
- VERSION = "0.1.0"
4
+ VERSION = "1.0.0"
5
5
  end
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, subheaps, parent, prev_sibling, next_sibling)
50
+ def initialize(elem, priority)
10
51
  @elem = elem
11
52
  @priority = priority
12
- @subheaps = subheaps
13
- @parent = parent
14
- @prev_sibling = prev_sibling
15
- @next_sibling = 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, nil, nil, nil, nil)
89
+ node = Node.new(elem, priority)
37
90
  @nodes[elem] = node
38
- @root = meld(@root, node)
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 [ArgumEntError] if the heap is empty
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 heap is not in heap or the new priority is less prioritary
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(node)
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 top of the heap
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 heap is not in heap
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(node)
201
+ node.remove_from_parents_list!
126
202
  new_heap = merge_pairs(node.subheaps)
127
203
  if new_heap
128
- new_heap.prev_sibling = nil
129
- new_heap.next_sibling = nil
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
- private
138
-
139
- def remove_from_parents_list(node)
140
- if node.prev_sibling
141
- node.prev_sibling.next_sibling = node.next_sibling
142
- node.next_sibling.prev_sibling = node.prev_sibling if node.next_sibling
143
- elsif node.parent.subheaps.equal?(node)
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
- def meld(left, right)
154
- return right if left.nil?
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
- # Non-recursive implementation of method described in https://en.wikipedia.org/wiki/Pairing_heap#delete-min
173
- def merge_pairs(heaps)
174
- return nil if heaps.nil?
175
- return heaps if heaps.next_sibling.nil?
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
- # [H1, H2, H3, H4, H5, H6, H7] => [H1H2, H3H4, H5H6, H7]
178
- stack = []
179
- current = heaps
180
- while current
181
- prev = current
182
- current = current.next_sibling
183
- unless current
184
- stack << prev
185
- break
186
- end
187
- next_val = current.next_sibling
188
- stack << meld(prev, current)
189
- current = next_val
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
- # [H1H2, H3H4, H5H6, H7]
193
- # [H1H2, H3H4, H5H67]
194
- # [H1H2, H3H45H67]
195
- # [H1H2H3H45H67]
196
- # return H1H2H3H45H67
197
- while true
198
- right = stack.pop
199
- return right if stack.empty?
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
- left = stack.pop
202
- stack << meld(left, right)
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 heap is not in the heap
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 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"
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"] = spec.homepage
18
- spec.metadata["source_code_uri"] = spec.homepage
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: 0.1.0
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: 2021-02-19 00:00:00.000000000 Z
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 Ruby with support for changing priority
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.2.3
84
+ rubygems_version: 3.3.3
84
85
  signing_key:
85
86
  specification_version: 4
86
- summary: Performant priority queue in pure Ruby with support for changing priority
87
+ summary: Performant priority queue in pure ruby with support for changing priority
87
88
  test_files: []