1
1
module Concurrent
2
2
3
- # @!macro [attach] priority_queue
3
+ # @!macro [new] priority_queue
4
+ #
5
+ # A queue collection in which the elements are sorted based on their
6
+ # comparison (spaceship) operator `<=>`. Items are added to the queue
7
+ # at a position relative to their priority. On removal the element
8
+ # with the "highest" priority is removed. By default the sort order is
9
+ # from highest to lowest, but a lowest-to-highest sort order can be
10
+ # set on construction.
11
+ #
12
+ # The API is based on the `Queue` class from the Ruby standard library.
13
+ #
14
+ # The pure Ruby implementation, `MutexPriorityQueue` uses a heap algorithm
15
+ # stored in an array. The algorithm is based on the work of Robert Sedgewick
16
+ # and Kevin Wayne.
17
+ #
18
+ # The JRuby native implementation is a thin wrapper around the standard
19
+ # library `java.util.PriorityQueue`.
20
+ #
21
+ # When running under JRuby the class `PriorityQueue` extends `JavaPriorityQueue`.
22
+ # When running under all other interpreters it extends `MutexPriorityQueue`.
23
+ #
24
+ # @note This implementation is *not* thread safe and performs no blocking.
4
25
#
5
- # @see http://ruby-doc.org/stdlib-2.0.0/libdoc/thread/rdoc/Queue.html
6
26
# @see http://en.wikipedia.org/wiki/Priority_queue
27
+ # @see http://ruby-doc.org/stdlib-2.0.0/libdoc/thread/rdoc/Queue.html
28
+ #
29
+ # @see http://algs4.cs.princeton.edu/24pq/index.php#2.6
7
30
# @see http://algs4.cs.princeton.edu/24pq/MaxPQ.java.html
31
+ #
32
+ # @see http://docs.oracle.com/javase/7/docs/api/java/util/PriorityQueue.html
8
33
class MutexPriorityQueue
9
34
10
- attr_reader :length
11
- alias_method :size , :length
12
-
35
+ # Create a new priority queue with no items.
36
+ #
37
+ # @param [Hash] opts the options for creating the queue
38
+ # @option opts [Symbol] :order (:max) dictates the order in which items are
39
+ # stored: from highest to lowest when `:max` or `:high`; from lowest to
40
+ # highest when `:min` or `:low`
13
41
def initialize ( opts = { } )
14
42
order = opts . fetch ( :order , :max )
15
- @comparator = [ :min , :low ] . include? ( order ) ? 1 : - 1
43
+ @comparator = [ :min , :low ] . include? ( order ) ? - 1 : 1
16
44
clear
17
45
end
18
46
47
+ # Removes all of the elements from this priority queue.
19
48
def clear
20
49
@queue = [ nil ]
21
50
@length = 0
22
51
true
23
52
end
24
53
54
+ # Deletes all items from `self` that are equal to `item`.
55
+ #
56
+ # @param [Object] item the item to be removed from the queue
57
+ # @return [Object] true if the item is found else false
25
58
def delete ( item )
26
59
original_length = @length
27
60
k = 1
@@ -38,19 +71,44 @@ def delete(item)
38
71
@length != original_length
39
72
end
40
73
74
+ # Returns `true` if `self` contains no elements.
75
+ #
76
+ # @return [Boolean] true if there are no items in the queue else false
41
77
def empty?
42
78
size == 0
43
79
end
44
80
81
+ # Returns `true` if the given item is present in `self` (that is, if any
82
+ # element == `item`), otherwise returns false.
83
+ #
84
+ # @param [Object] item the item to search for
85
+ #
86
+ # @return [Boolean] true if the item is found else false
45
87
def include? ( item )
46
88
@queue . include? ( item )
47
89
end
48
90
alias_method :has_priority? , :include?
49
91
92
+ # The current length of the queue.
93
+ #
94
+ # @return [Fixnum] the number of items in the queue
95
+ def length
96
+ @length
97
+ end
98
+ alias_method :size , :length
99
+
100
+ # Retrieves, but does not remove, the head of this queue, or returns `nil`
101
+ # if this queue is empty.
102
+ #
103
+ # @return [Object] the head of the queue or `nil` when empty
50
104
def peek
51
105
@queue [ 1 ]
52
106
end
53
107
108
+ # Retrieves and removes the head of this queue, or returns `nil` if this
109
+ # queue is empty.
110
+ #
111
+ # @return [Object] the head of the queue or `nil` when empty
54
112
def pop
55
113
max = @queue [ 1 ]
56
114
swap ( 1 , @length )
@@ -62,6 +120,9 @@ def pop
62
120
alias_method :deq , :pop
63
121
alias_method :shift , :pop
64
122
123
+ # Inserts the specified element into this priority queue.
124
+ #
125
+ # @param [Object] item the item to insert onto the queue
65
126
def push ( item )
66
127
@length += 1
67
128
@queue << item
@@ -71,6 +132,12 @@ def push(item)
71
132
alias_method :<< , :push
72
133
alias_method :enq , :push
73
134
135
+ # Create a new priority queue from the given list.
136
+ #
137
+ # @param [Enumerable] list the list to build the queue from
138
+ # @param [Hash] opts the options for creating the queue
139
+ #
140
+ # @return [PriorityQueue] the newly created and populated queue
74
141
def self . from_list ( list , opts = { } )
75
142
queue = new ( opts )
76
143
list . each { |item | queue << item }
@@ -79,27 +146,53 @@ def self.from_list(list, opts = {})
79
146
80
147
protected
81
148
149
+ # Exchange the values at the given indexes within the internal array.
150
+ #
151
+ # @param [Integer] x the first index to swap
152
+ # @param [Integer] y the second index to swap
153
+ #
154
+ # @!visibility private
82
155
def swap ( x , y )
83
156
temp = @queue [ x ]
84
157
@queue [ x ] = @queue [ y ]
85
158
@queue [ y ] = temp
86
159
end
87
160
88
- def prioritize? ( x , y )
161
+ # Are the items at the given indexes ordered based on the priority
162
+ # order specified at construction?
163
+ #
164
+ # @param [Integer] x the first index from which to retrieve a comparable value
165
+ # @param [Integer] y the second index from which to retrieve a comparable value
166
+ #
167
+ # @return [Boolean] true if the two elements are in the correct priority order
168
+ # else false
169
+ #
170
+ # @!visibility private
171
+ def ordered? ( x , y )
89
172
( @queue [ x ] <=> @queue [ y ] ) == @comparator
90
173
end
91
174
175
+ # Percolate down to maintain heap invariant.
176
+ #
177
+ # @param [Integer] k the index at which to start the percolation
178
+ #
179
+ # @!visibility private
92
180
def sink ( k )
93
181
while ( j = ( 2 * k ) ) <= @length do
94
- j += 1 if j < @length && prioritize ?( j , j +1 )
95
- break unless prioritize ?( k , j )
182
+ j += 1 if j < @length && ! ordered ?( j , j +1 )
183
+ break if ordered ?( k , j )
96
184
swap ( k , j )
97
185
k = j
98
186
end
99
187
end
100
188
189
+ # Percolate up to maintain heap invariant.
190
+ #
191
+ # @param [Integer] k the index at which to start the percolation
192
+ #
193
+ # @!visibility private
101
194
def swim ( k )
102
- while k > 1 && prioritize ?( k /2 , k ) do
195
+ while k > 1 && ! ordered ?( k /2 , k ) do
103
196
swap ( k , k /2 )
104
197
k = k /2
105
198
end
@@ -109,10 +202,14 @@ def swim(k)
109
202
if RUBY_PLATFORM == 'java'
110
203
111
204
# @!macro priority_queue
112
- #
113
- # @see http://docs.oracle.com/javase/7/docs/api/java/util/PriorityQueue.html
114
205
class JavaPriorityQueue
115
206
207
+ # Create a new priority queue with no items.
208
+ #
209
+ # @param [Hash] opts the options for creating the queue
210
+ # @option opts [Symbol] :order (:max) dictates the order in which items are
211
+ # stored: from highest to lowest when `:max` or `:high`; from lowest to
212
+ # highest when `:min` or `:low`
116
213
def initialize ( opts = { } )
117
214
order = opts . fetch ( :order , :max )
118
215
if [ :min , :low ] . include? ( order )
@@ -122,11 +219,16 @@ def initialize(opts = {})
122
219
end
123
220
end
124
221
222
+ # Removes all of the elements from this priority queue.
125
223
def clear
126
224
@queue . clear
127
225
true
128
226
end
129
227
228
+ # Deletes all items from `self` that are equal to `item`.
229
+ #
230
+ # @param [Object] item the item to be removed from the queue
231
+ # @return [Object] true if the item is found else false
130
232
def delete ( item )
131
233
found = false
132
234
while @queue . remove ( item ) do
@@ -135,36 +237,65 @@ def delete(item)
135
237
found
136
238
end
137
239
240
+ # Returns `true` if `self` contains no elements.
241
+ #
242
+ # @return [Boolean] true if there are no items in the queue else false
138
243
def empty?
139
244
@queue . size == 0
140
245
end
141
246
247
+ # Returns `true` if the given item is present in `self` (that is, if any
248
+ # element == `item`), otherwise returns false.
249
+ #
250
+ # @param [Object] item the item to search for
251
+ #
252
+ # @return [Boolean] true if the item is found else false
142
253
def include? ( item )
143
254
@queue . contains ( item )
144
255
end
145
256
alias_method :has_priority? , :include?
146
257
258
+ # The current length of the queue.
259
+ #
260
+ # @return [Fixnum] the number of items in the queue
147
261
def length
148
262
@queue . size
149
263
end
150
264
alias_method :size , :length
151
265
266
+ # Retrieves, but does not remove, the head of this queue, or returns `nil`
267
+ # if this queue is empty.
268
+ #
269
+ # @return [Object] the head of the queue or `nil` when empty
152
270
def peek
153
271
@queue . peek
154
272
end
155
273
274
+ # Retrieves and removes the head of this queue, or returns `nil` if this
275
+ # queue is empty.
276
+ #
277
+ # @return [Object] the head of the queue or `nil` when empty
156
278
def pop
157
279
@queue . poll
158
280
end
159
281
alias_method :deq , :pop
160
282
alias_method :shift , :pop
161
283
284
+ # Inserts the specified element into this priority queue.
285
+ #
286
+ # @param [Object] item the item to insert onto the queue
162
287
def push ( item )
163
288
@queue . add ( item )
164
289
end
165
290
alias_method :<< , :push
166
291
alias_method :enq , :push
167
292
293
+ # Create a new priority queue from the given list.
294
+ #
295
+ # @param [Enumerable] list the list to build the queue from
296
+ # @param [Hash] opts the options for creating the queue
297
+ #
298
+ # @return [PriorityQueue] the newly created and populated queue
168
299
def self . from_list ( list , opts = { } )
169
300
queue = new ( opts )
170
301
list . each { |item | queue << item }
0 commit comments