akr-depq 0.1
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.
- data/README +181 -0
- data/depq.rb +1520 -0
- data/test-depq.rb +772 -0
- metadata +55 -0
data/README
ADDED
@@ -0,0 +1,181 @@
|
|
1
|
+
Depq - Feature Rich Double-Ended Priority Queue.
|
2
|
+
|
3
|
+
= Features
|
4
|
+
|
5
|
+
* queue - you can insert and delete values
|
6
|
+
* priority - you can get a value with minimum priority
|
7
|
+
* double-ended - you can get a value with maximum priority too
|
8
|
+
* stable - you don't need to maintain timestamps yourself
|
9
|
+
* update priority - usable for Dijkstra's shortest path algorithm and various graph algorithms
|
10
|
+
* implicit binary heap - most operations are O(log n) at worst
|
11
|
+
|
12
|
+
= Introduction
|
13
|
+
|
14
|
+
== Simple Insertion/Deletion
|
15
|
+
|
16
|
+
You can insert values into a Depq object.
|
17
|
+
You can deletes the values from the object from ascending/descending order.
|
18
|
+
delete_min deletes the minimum value.
|
19
|
+
It is used for ascending order.
|
20
|
+
|
21
|
+
pd = Depq.new
|
22
|
+
pd.insert "durian"
|
23
|
+
pd.insert "banana"
|
24
|
+
p pd.delete_min #=> "banana"
|
25
|
+
pd.insert "orange"
|
26
|
+
pd.insert "apple"
|
27
|
+
pd.insert "melon"
|
28
|
+
p pd.delete_min #=> "apple"
|
29
|
+
p pd.delete_min #=> "durian"
|
30
|
+
p pd.delete_min #=> "melon"
|
31
|
+
p pd.delete_min #=> "orange"
|
32
|
+
p pd.delete_min #=> nil
|
33
|
+
|
34
|
+
delete_max is similar to delete_min except it deletes maximum element
|
35
|
+
instead of minimum.
|
36
|
+
It is used for descending order.
|
37
|
+
|
38
|
+
== The Order
|
39
|
+
|
40
|
+
The order is defined by the priorities corresnponds to the values and
|
41
|
+
comparison operator specified for the queue.
|
42
|
+
|
43
|
+
pd = Depq.new(:casecmp) # use casecmp instead of <=>.
|
44
|
+
pd.inesrt 1, "Foo" # specify the priority for 1 as "Foo"
|
45
|
+
pd.insert 2, "bar"
|
46
|
+
pd.insert 3, "Baz"
|
47
|
+
p pd.delete_min #=> 2 # "bar" is minimum
|
48
|
+
p pd.delete_min #=> 3
|
49
|
+
p pd.delete_min #=> 1 # "Foo" is maximum
|
50
|
+
p pd.delete_min #=> nil
|
51
|
+
|
52
|
+
If there are multiple values with same priority, subpriority is used to compare them.
|
53
|
+
subpriority is an integer which can be specified by 3rd argument of insert.
|
54
|
+
If it is not specified, total number of inserted elements is used.
|
55
|
+
So Depq is "stable" with delete_min.
|
56
|
+
The element inserted first is minimum and deleted first.
|
57
|
+
|
58
|
+
pd = Depq.new
|
59
|
+
pd.insert "a", 1 # "a", "c" and "e" has same priority: 1
|
60
|
+
pd.insert "b", 0 # "b", "d" and "f" has same priority: 0
|
61
|
+
pd.insert "c", 1
|
62
|
+
pd.insert "d", 0
|
63
|
+
pd.insert "e", 1
|
64
|
+
pd.insert "f", 0
|
65
|
+
p pd.delete_min #=> "b" first element with priority 0
|
66
|
+
p pd.delete_min #=> "d"
|
67
|
+
p pd.delete_min #=> "f" last element with priority 0
|
68
|
+
p pd.delete_min #=> "a" first element with priority 1
|
69
|
+
p pd.delete_min #=> "c"
|
70
|
+
p pd.delete_min #=> "e" last element with priority 1
|
71
|
+
|
72
|
+
Note that delete_max is also stable.
|
73
|
+
This means delete_max deletes the element with maximum priority with "minimum" subpriority.
|
74
|
+
|
75
|
+
== Update Element
|
76
|
+
|
77
|
+
An inserted element can be modified and/or deleted.
|
78
|
+
This is done using Depq::Locator object.
|
79
|
+
It is returned by insert, find_min_locator, etc.
|
80
|
+
|
81
|
+
pd = Depq.new
|
82
|
+
d = pd.insert "durian", 1
|
83
|
+
m = pd.insert "mangosteen", 2
|
84
|
+
c = pd.insert "cherry", 3
|
85
|
+
p m #=> #<Depq::Locator: "mangosteen":2>
|
86
|
+
p m.value #=> "mangosteen"
|
87
|
+
p m.priority #=> 2
|
88
|
+
p pd.find_min #=> "durian"
|
89
|
+
p pd.find_min_locator #=> #<Depq::Locator: "durian":1>
|
90
|
+
m.update("mangosteen", 0)
|
91
|
+
p pd.find_min #=> "mangosteen"
|
92
|
+
p pd.find_min_locator #=> #<Depq::Locator: "mangosteen":0>
|
93
|
+
pd.delete_element d
|
94
|
+
p pd.delete_min #=> "mangosteen"
|
95
|
+
p pd.delete_min #=> "cherry"
|
96
|
+
p pd.delete_min #=> nil
|
97
|
+
|
98
|
+
For example, this feature can be used for graph algorithms
|
99
|
+
such as Dijkstra's shortest path finding algorithm,
|
100
|
+
A* search algorithm, etc.
|
101
|
+
|
102
|
+
def dijkstra_shortest_path(start, edges)
|
103
|
+
h = {}
|
104
|
+
edges.each {|v1, v2, w|
|
105
|
+
(h[v1] ||= []) << [v2, w]
|
106
|
+
}
|
107
|
+
h.default = []
|
108
|
+
q = Depq.new
|
109
|
+
visited = {start => q.insert([start], 0)}
|
110
|
+
until q.empty?
|
111
|
+
path, w1 = q.delete_min_priority
|
112
|
+
v1 = path.last
|
113
|
+
h[v1].each {|v2, w2|
|
114
|
+
if !visited[v2]
|
115
|
+
visited[v2] = q.insert(path+[v2], w1 + w2)
|
116
|
+
elsif w1 + w2 < visited[v2].priority
|
117
|
+
visited[v2].update(path+[v2], w1 + w2) # update val/prio
|
118
|
+
end
|
119
|
+
}
|
120
|
+
end
|
121
|
+
result = []
|
122
|
+
visited.each_value {|loc|
|
123
|
+
result << [loc.value, loc.priority]
|
124
|
+
}
|
125
|
+
result
|
126
|
+
end
|
127
|
+
|
128
|
+
E = [
|
129
|
+
['A', 'B', 2],
|
130
|
+
['A', 'C', 4],
|
131
|
+
['B', 'C', 1],
|
132
|
+
['C', 'B', 2],
|
133
|
+
['B', 'D', 3],
|
134
|
+
['C', 'D', 1],
|
135
|
+
]
|
136
|
+
p dijkstra_shortest_path('A', E)
|
137
|
+
#=> [[["A"], 0],
|
138
|
+
# [["A", "B"], 2],
|
139
|
+
# [["A", "B", "C"], 3],
|
140
|
+
# [["A", "B", "C", "D"], 4]]
|
141
|
+
|
142
|
+
= Internal Heap Algorithm and Performance Tips
|
143
|
+
|
144
|
+
Depq uses min-heap or max-heap internally.
|
145
|
+
When delete_min is used, min-heap is constructed and max-heap is destructed.
|
146
|
+
When delete_max is used, max-heap is constructed and min-heap is destructed.
|
147
|
+
So mixing delete_min and delete_max causes bad performance.
|
148
|
+
In future, min-max-heap may be implemented to avoid this problem.
|
149
|
+
min-max-heap will be used when delete_min and delete_max is used both.
|
150
|
+
(Because min-max-heap is slower than min-heap/max-heap.)
|
151
|
+
|
152
|
+
= Author
|
153
|
+
|
154
|
+
Tanaka Akira <akr@fsij.org>
|
155
|
+
|
156
|
+
= License
|
157
|
+
|
158
|
+
Redistribution and use in source and binary forms, with or without
|
159
|
+
modification, are permitted provided that the following conditions are met:
|
160
|
+
|
161
|
+
(1) Redistributions of source code must retain the above copyright notice, this
|
162
|
+
list of conditions and the following disclaimer.
|
163
|
+
(2) Redistributions in binary form must reproduce the above copyright notice,
|
164
|
+
this list of conditions and the following disclaimer in the documentation
|
165
|
+
and/or other materials provided with the distribution.
|
166
|
+
(3) The name of the author may not be used to endorse or promote products
|
167
|
+
derived from this software without specific prior written permission.
|
168
|
+
|
169
|
+
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
170
|
+
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
171
|
+
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
172
|
+
EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
173
|
+
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
174
|
+
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
175
|
+
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
176
|
+
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
177
|
+
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
178
|
+
OF SUCH DAMAGE.
|
179
|
+
|
180
|
+
(The modified BSD licence)
|
181
|
+
|
data/depq.rb
ADDED
@@ -0,0 +1,1520 @@
|
|
1
|
+
# depq.rb - Feature Rich Double-Ended Priority Queue.
|
2
|
+
#
|
3
|
+
# Copyright (C) 2009 Tanaka Akira <akr@fsij.org>
|
4
|
+
#
|
5
|
+
# Redistribution and use in source and binary forms, with or without
|
6
|
+
# modification, are permitted provided that the following conditions are met:
|
7
|
+
#
|
8
|
+
# 1. Redistributions of source code must retain the above copyright notice, this
|
9
|
+
# list of conditions and the following disclaimer.
|
10
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
# this list of conditions and the following disclaimer in the documentation
|
12
|
+
# and/or other materials provided with the distribution.
|
13
|
+
# 3. The name of the author may not be used to endorse or promote products
|
14
|
+
# derived from this software without specific prior written permission.
|
15
|
+
#
|
16
|
+
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
17
|
+
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
18
|
+
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
19
|
+
# EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
20
|
+
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
21
|
+
# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
22
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
23
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
24
|
+
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
25
|
+
# OF SUCH DAMAGE.
|
26
|
+
|
27
|
+
# Depq - Feature Rich Double-Ended Priority Queue.
|
28
|
+
#
|
29
|
+
# = Features
|
30
|
+
#
|
31
|
+
# * queue - you can insert and delete values
|
32
|
+
# * priority - you can get a value with minimum priority
|
33
|
+
# * double-ended - you can get a value with maximum priority too
|
34
|
+
# * stable - you don't need to maintain timestamps yourself
|
35
|
+
# * update priority - usable for Dijkstra's shortest path algorithm and various graph algorithms
|
36
|
+
# * implicit binary heap - most operations are O(log n) at worst
|
37
|
+
#
|
38
|
+
# = Introduction
|
39
|
+
#
|
40
|
+
# == Simple Insertion/Deletion
|
41
|
+
#
|
42
|
+
# You can insert values into a Depq object.
|
43
|
+
# You can deletes the values from the object from ascending/descending order.
|
44
|
+
# delete_min deletes the minimum value.
|
45
|
+
# It is used for ascending order.
|
46
|
+
#
|
47
|
+
# pd = Depq.new
|
48
|
+
# pd.insert "durian"
|
49
|
+
# pd.insert "banana"
|
50
|
+
# p pd.delete_min #=> "banana"
|
51
|
+
# pd.insert "orange"
|
52
|
+
# pd.insert "apple"
|
53
|
+
# pd.insert "melon"
|
54
|
+
# p pd.delete_min #=> "apple"
|
55
|
+
# p pd.delete_min #=> "durian"
|
56
|
+
# p pd.delete_min #=> "melon"
|
57
|
+
# p pd.delete_min #=> "orange"
|
58
|
+
# p pd.delete_min #=> nil
|
59
|
+
#
|
60
|
+
# delete_max is similar to delete_min except it deletes maximum element
|
61
|
+
# instead of minimum.
|
62
|
+
# It is used for descending order.
|
63
|
+
#
|
64
|
+
# == The Order
|
65
|
+
#
|
66
|
+
# The order is defined by the priorities corresnponds to the values and
|
67
|
+
# comparison operator specified for the queue.
|
68
|
+
#
|
69
|
+
# pd = Depq.new(:casecmp) # use casecmp instead of <=>.
|
70
|
+
# pd.inesrt 1, "Foo" # specify the priority for 1 as "Foo"
|
71
|
+
# pd.insert 2, "bar"
|
72
|
+
# pd.insert 3, "Baz"
|
73
|
+
# p pd.delete_min #=> 2 # "bar" is minimum
|
74
|
+
# p pd.delete_min #=> 3
|
75
|
+
# p pd.delete_min #=> 1 # "Foo" is maximum
|
76
|
+
# p pd.delete_min #=> nil
|
77
|
+
#
|
78
|
+
# If there are multiple values with same priority, subpriority is used to compare them.
|
79
|
+
# subpriority is an integer which can be specified by 3rd argument of insert.
|
80
|
+
# If it is not specified, total number of inserted elements is used.
|
81
|
+
# So Depq is "stable" with delete_min.
|
82
|
+
# The element inserted first is minimum and deleted first.
|
83
|
+
#
|
84
|
+
# pd = Depq.new
|
85
|
+
# pd.insert "a", 1 # "a", "c" and "e" has same priority: 1
|
86
|
+
# pd.insert "b", 0 # "b", "d" and "f" has same priority: 0
|
87
|
+
# pd.insert "c", 1
|
88
|
+
# pd.insert "d", 0
|
89
|
+
# pd.insert "e", 1
|
90
|
+
# pd.insert "f", 0
|
91
|
+
# p pd.delete_min #=> "b" first element with priority 0
|
92
|
+
# p pd.delete_min #=> "d"
|
93
|
+
# p pd.delete_min #=> "f" last element with priority 0
|
94
|
+
# p pd.delete_min #=> "a" first element with priority 1
|
95
|
+
# p pd.delete_min #=> "c"
|
96
|
+
# p pd.delete_min #=> "e" last element with priority 1
|
97
|
+
#
|
98
|
+
# Note that delete_max is also stable.
|
99
|
+
# This means delete_max deletes the element with maximum priority with "minimum" subpriority.
|
100
|
+
#
|
101
|
+
# == Update Element
|
102
|
+
#
|
103
|
+
# An inserted element can be modified and/or deleted.
|
104
|
+
# This is done using Depq::Locator object.
|
105
|
+
# It is returned by insert, find_min_locator, etc.
|
106
|
+
#
|
107
|
+
# pd = Depq.new
|
108
|
+
# d = pd.insert "durian", 1
|
109
|
+
# m = pd.insert "mangosteen", 2
|
110
|
+
# c = pd.insert "cherry", 3
|
111
|
+
# p m #=> #<Depq::Locator: "mangosteen":2>
|
112
|
+
# p m.value #=> "mangosteen"
|
113
|
+
# p m.priority #=> 2
|
114
|
+
# p pd.find_min #=> "durian"
|
115
|
+
# p pd.find_min_locator #=> #<Depq::Locator: "durian":1>
|
116
|
+
# m.update("mangosteen", 0)
|
117
|
+
# p pd.find_min #=> "mangosteen"
|
118
|
+
# p pd.find_min_locator #=> #<Depq::Locator: "mangosteen":0>
|
119
|
+
# pd.delete_element d
|
120
|
+
# p pd.delete_min #=> "mangosteen"
|
121
|
+
# p pd.delete_min #=> "cherry"
|
122
|
+
# p pd.delete_min #=> nil
|
123
|
+
#
|
124
|
+
# For example, this feature can be used for graph algorithms
|
125
|
+
# such as Dijkstra's shortest path finding algorithm,
|
126
|
+
# A* search algorithm, etc.
|
127
|
+
#
|
128
|
+
# def dijkstra_shortest_path(start, edges)
|
129
|
+
# h = {}
|
130
|
+
# edges.each {|v1, v2, w|
|
131
|
+
# (h[v1] ||= []) << [v2, w]
|
132
|
+
# }
|
133
|
+
# h.default = []
|
134
|
+
# q = Depq.new
|
135
|
+
# visited = {start => q.insert([start], 0)}
|
136
|
+
# until q.empty?
|
137
|
+
# path, w1 = q.delete_min_priority
|
138
|
+
# v1 = path.last
|
139
|
+
# h[v1].each {|v2, w2|
|
140
|
+
# if !visited[v2]
|
141
|
+
# visited[v2] = q.insert(path+[v2], w1 + w2)
|
142
|
+
# elsif w1 + w2 < visited[v2].priority
|
143
|
+
# visited[v2].update(path+[v2], w1 + w2) # update val/prio
|
144
|
+
# end
|
145
|
+
# }
|
146
|
+
# end
|
147
|
+
# result = []
|
148
|
+
# visited.each_value {|loc|
|
149
|
+
# result << [loc.value, loc.priority]
|
150
|
+
# }
|
151
|
+
# result
|
152
|
+
# end
|
153
|
+
#
|
154
|
+
# E = [
|
155
|
+
# ['A', 'B', 2],
|
156
|
+
# ['A', 'C', 4],
|
157
|
+
# ['B', 'C', 1],
|
158
|
+
# ['C', 'B', 2],
|
159
|
+
# ['B', 'D', 3],
|
160
|
+
# ['C', 'D', 1],
|
161
|
+
# ]
|
162
|
+
# p dijkstra_shortest_path('A', E)
|
163
|
+
# #=> [[["A"], 0],
|
164
|
+
# # [["A", "B"], 2],
|
165
|
+
# # [["A", "B", "C"], 3],
|
166
|
+
# # [["A", "B", "C", "D"], 4]]
|
167
|
+
#
|
168
|
+
# = Internal Heap Algorithm and Performance Tips
|
169
|
+
#
|
170
|
+
# Depq uses min-heap or max-heap internally.
|
171
|
+
# When delete_min is used, min-heap is constructed and max-heap is destructed.
|
172
|
+
# When delete_max is used, max-heap is constructed and min-heap is destructed.
|
173
|
+
# So mixing delete_min and delete_max causes bad performance.
|
174
|
+
# In future, min-max-heap may be implemented to avoid this problem.
|
175
|
+
# min-max-heap will be used when delete_min and delete_max is used both.
|
176
|
+
# (Because min-max-heap is slower than min-heap/max-heap.)
|
177
|
+
#
|
178
|
+
class Depq
|
179
|
+
include Enumerable
|
180
|
+
|
181
|
+
Locator = Struct.new(:value, :pdeque_or_subpriority, :index_or_priority)
|
182
|
+
class Locator
|
183
|
+
|
184
|
+
# if pdeque_or_subpriority is Depq
|
185
|
+
# pdeque_or_subpriority is pdeque
|
186
|
+
# index_or_priority is index
|
187
|
+
# else
|
188
|
+
# pdeque_or_subpriority is subpriority
|
189
|
+
# index_or_priority is priority
|
190
|
+
# end
|
191
|
+
#
|
192
|
+
# only 3 fields for memory efficiency.
|
193
|
+
|
194
|
+
private :value=
|
195
|
+
private :pdeque_or_subpriority
|
196
|
+
private :pdeque_or_subpriority=
|
197
|
+
private :index_or_priority
|
198
|
+
private :index_or_priority=
|
199
|
+
|
200
|
+
private :to_a
|
201
|
+
private :values
|
202
|
+
private :size
|
203
|
+
private :length
|
204
|
+
private :each
|
205
|
+
private :each_pair
|
206
|
+
private :[]
|
207
|
+
private :[]=
|
208
|
+
private :values_at
|
209
|
+
private :members
|
210
|
+
private :select
|
211
|
+
|
212
|
+
Enumerable.instance_methods.each {|m|
|
213
|
+
private m
|
214
|
+
}
|
215
|
+
|
216
|
+
define_method(:==, Object.instance_method(:eql?))
|
217
|
+
define_method(:eql?, Object.instance_method(:eql?))
|
218
|
+
define_method(:hash, Object.instance_method(:hash))
|
219
|
+
|
220
|
+
include Comparable
|
221
|
+
|
222
|
+
# Create a Depq::Locator object.
|
223
|
+
def initialize(value, priority=value, subpriority=nil)
|
224
|
+
super value, subpriority, priority
|
225
|
+
end
|
226
|
+
|
227
|
+
def inspect
|
228
|
+
prio = self.priority
|
229
|
+
if self.value == prio
|
230
|
+
s = self.value.inspect
|
231
|
+
else
|
232
|
+
s = "#{self.value.inspect}:#{prio.inspect}"
|
233
|
+
end
|
234
|
+
if in_queue?
|
235
|
+
"\#<#{self.class}: #{s}>"
|
236
|
+
else
|
237
|
+
"\#<#{self.class}: #{s} (no queue)>"
|
238
|
+
end
|
239
|
+
end
|
240
|
+
alias to_s inspect
|
241
|
+
|
242
|
+
def initialize_copy(obj) # :nodoc:
|
243
|
+
raise TypeError, "can not duplicated"
|
244
|
+
end
|
245
|
+
|
246
|
+
# returns true if the locator is in a queue.
|
247
|
+
def in_queue?
|
248
|
+
pdeque_or_subpriority().kind_of? Depq
|
249
|
+
end
|
250
|
+
|
251
|
+
# returns the queue.
|
252
|
+
#
|
253
|
+
# nil is returned if the locator is not in a pdeque.
|
254
|
+
def pdeque
|
255
|
+
in_queue? ? pdeque_or_subpriority() : nil
|
256
|
+
end
|
257
|
+
alias queue pdeque
|
258
|
+
|
259
|
+
def index
|
260
|
+
in_queue? ? index_or_priority() : nil
|
261
|
+
end
|
262
|
+
private :index
|
263
|
+
|
264
|
+
def index=(i)
|
265
|
+
if !in_queue?
|
266
|
+
raise ArgumentError, "not in queue"
|
267
|
+
end
|
268
|
+
self.index_or_priority = i
|
269
|
+
end
|
270
|
+
private :index=
|
271
|
+
|
272
|
+
# returns the priority.
|
273
|
+
def priority
|
274
|
+
if in_queue?
|
275
|
+
pd = pdeque_or_subpriority()
|
276
|
+
priority, subpriority = pd.send(:internal_get_priority, self)
|
277
|
+
priority
|
278
|
+
else
|
279
|
+
index_or_priority()
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
# returns the subpriority.
|
284
|
+
def subpriority
|
285
|
+
if in_queue?
|
286
|
+
pd = pdeque_or_subpriority()
|
287
|
+
priority, subpriority = pd.send(:internal_get_priority, self)
|
288
|
+
subpriority
|
289
|
+
else
|
290
|
+
pdeque_or_subpriority()
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
# update the value, priority and subpriority.
|
295
|
+
#
|
296
|
+
# subpriority cannot be nil if the locator is in a queue.
|
297
|
+
# So subpriority is not changed if subpriority is not specified or nil for a locator in a queue.
|
298
|
+
# subpriority is set to nil if subpriority is not specified or nil for a locator not in a queue.
|
299
|
+
#
|
300
|
+
# pd = Depq.new
|
301
|
+
# loc1 = pd.insert 1, 2, 3
|
302
|
+
# p [loc1.value, loc1.priority, loc1.subpriority] #=> [1, 2, 3]
|
303
|
+
# loc1.update(11, 12)
|
304
|
+
# p [loc1.value, loc1.priority, loc1.subpriority] #=> [11, 12, 3]
|
305
|
+
#
|
306
|
+
# loc2 = Depq::Locator.new(4, 5, 6)
|
307
|
+
# p [loc2.value, loc2.priority, loc2.subpriority] #=> [4, 5, 6]
|
308
|
+
# loc2.update(14, 15)
|
309
|
+
# p [loc2.value, loc2.priority, loc2.subpriority] #=> [14, 15, nil]
|
310
|
+
#
|
311
|
+
# This feature is called as decrease-key/increase-key in
|
312
|
+
# Computer Science terminology.
|
313
|
+
def update(value, priority=value, subpriority=nil)
|
314
|
+
subpriority = Integer(subpriority) if subpriority != nil
|
315
|
+
if in_queue?
|
316
|
+
pd = pdeque_or_subpriority()
|
317
|
+
if subpriority == nil
|
318
|
+
subpriority = self.subpriority
|
319
|
+
else
|
320
|
+
subpriority = Integer(subpriority)
|
321
|
+
end
|
322
|
+
pd.send(:internal_set_priority, self, priority, subpriority)
|
323
|
+
else
|
324
|
+
self.index_or_priority = priority
|
325
|
+
self.pdeque_or_subpriority = subpriority
|
326
|
+
end
|
327
|
+
self.value = value
|
328
|
+
nil
|
329
|
+
end
|
330
|
+
|
331
|
+
# update the value.
|
332
|
+
#
|
333
|
+
# This method doesn't change the priority and subpriority.
|
334
|
+
#
|
335
|
+
# pd = Depq.new
|
336
|
+
# loc = pd.insert 1, 2, 3
|
337
|
+
# p [loc.value, loc.priority, loc.subpriority] #=> [1, 2, 3]
|
338
|
+
# loc.update_value 10
|
339
|
+
# p [loc.value, loc.priority, loc.subpriority] #=> [10, 2, 3]
|
340
|
+
#
|
341
|
+
def update_value(value)
|
342
|
+
update(value, self.priority, self.subpriority)
|
343
|
+
end
|
344
|
+
|
345
|
+
# update the priority and subpriority.
|
346
|
+
#
|
347
|
+
# This method doesn't change the value.
|
348
|
+
#
|
349
|
+
# pd = Depq.new
|
350
|
+
# loc = pd.insert 1, 2, 3
|
351
|
+
# p [loc.value, loc.priority, loc.subpriority] #=> [1, 2, 3]
|
352
|
+
# loc.update_priority 10
|
353
|
+
# p [loc.value, loc.priority, loc.subpriority] #=> [1, 10, 3]
|
354
|
+
# loc.update_priority 20, 30
|
355
|
+
# p [loc.value, loc.priority, loc.subpriority] #=> [1, 20, 30]
|
356
|
+
#
|
357
|
+
def update_priority(priority, subpriority=nil)
|
358
|
+
update(self.value, priority, subpriority)
|
359
|
+
end
|
360
|
+
|
361
|
+
def internal_inserted(pdeque, index)
|
362
|
+
raise ArgumentError, "already inserted" if in_queue?
|
363
|
+
priority = index_or_priority()
|
364
|
+
self.pdeque_or_subpriority = pdeque
|
365
|
+
self.index_or_priority = index
|
366
|
+
priority
|
367
|
+
end
|
368
|
+
private :internal_inserted
|
369
|
+
|
370
|
+
def internal_deleted(priority, subpriority)
|
371
|
+
raise ArgumentError, "not inserted" if !in_queue?
|
372
|
+
self.index_or_priority = priority
|
373
|
+
self.pdeque_or_subpriority = subpriority
|
374
|
+
end
|
375
|
+
private :internal_deleted
|
376
|
+
|
377
|
+
end
|
378
|
+
|
379
|
+
# Create a Depq object.
|
380
|
+
#
|
381
|
+
# The optional argument, cmp, specify the method to compare priorities.
|
382
|
+
# It should be a symbol or a Proc which takes two arguments.
|
383
|
+
# If it is omitted, :<=> is used.
|
384
|
+
#
|
385
|
+
# pd = Depq.new
|
386
|
+
# pd.insert "Foo"
|
387
|
+
# pd.insert "bar"
|
388
|
+
# p pd.delete_min #=> "Foo"
|
389
|
+
# p pd.delete_min #=> "bar"
|
390
|
+
#
|
391
|
+
# pd = Depq.new(:casecmp)
|
392
|
+
# pd.insert "Foo"
|
393
|
+
# pd.insert "bar"
|
394
|
+
# p pd.delete_min #=> "bar"
|
395
|
+
# p pd.delete_min #=> "Foo"
|
396
|
+
#
|
397
|
+
# pd = Depq.new(lambda {|a,b| a.casecmp(b) })
|
398
|
+
# pd.insert "Foo"
|
399
|
+
# pd.insert "bar"
|
400
|
+
# p pd.delete_min #=> "bar"
|
401
|
+
# p pd.delete_min #=> "Foo"
|
402
|
+
#
|
403
|
+
def initialize(cmp = :<=>)
|
404
|
+
@cmp = cmp
|
405
|
+
@ary = []
|
406
|
+
@heapsize = 0
|
407
|
+
@mode = nil
|
408
|
+
@totalcount = 0
|
409
|
+
#@subpriority_generator = nil
|
410
|
+
end
|
411
|
+
|
412
|
+
# :stopdoc:
|
413
|
+
ARY_SLICE_SIZE = 3
|
414
|
+
# :startdoc:
|
415
|
+
|
416
|
+
def get_entry(i)
|
417
|
+
locator = @ary[i*ARY_SLICE_SIZE+0]
|
418
|
+
priority = @ary[i*ARY_SLICE_SIZE+1]
|
419
|
+
subpriority = @ary[i*ARY_SLICE_SIZE+2]
|
420
|
+
[locator, priority, subpriority]
|
421
|
+
end
|
422
|
+
private :get_entry
|
423
|
+
|
424
|
+
def set_entry(i, locator, priority, subpriority)
|
425
|
+
tmp = Array.new(ARY_SLICE_SIZE)
|
426
|
+
tmp[0] = locator
|
427
|
+
tmp[1] = priority
|
428
|
+
tmp[2] = subpriority
|
429
|
+
@ary[i*ARY_SLICE_SIZE, ARY_SLICE_SIZE] = tmp
|
430
|
+
end
|
431
|
+
private :set_entry
|
432
|
+
|
433
|
+
def delete_entry(i)
|
434
|
+
locator, priority, subpriority = @ary[i*ARY_SLICE_SIZE, ARY_SLICE_SIZE]
|
435
|
+
@ary[i*ARY_SLICE_SIZE, ARY_SLICE_SIZE] = []
|
436
|
+
[locator, priority, subpriority]
|
437
|
+
end
|
438
|
+
private :delete_entry
|
439
|
+
|
440
|
+
def each_entry
|
441
|
+
0.upto(self.size-1) {|i|
|
442
|
+
ei = @ary[i*ARY_SLICE_SIZE+0]
|
443
|
+
pi = @ary[i*ARY_SLICE_SIZE+1]
|
444
|
+
si = @ary[i*ARY_SLICE_SIZE+2]
|
445
|
+
yield ei, pi, si
|
446
|
+
}
|
447
|
+
end
|
448
|
+
private :each_entry
|
449
|
+
|
450
|
+
def min_mode
|
451
|
+
if @mode != MinHeap
|
452
|
+
@mode = MinHeap
|
453
|
+
@heapsize = @mode.heapify(self, @ary)
|
454
|
+
elsif @heapsize < self.size
|
455
|
+
@heapsize = @mode.heapify(self, @ary, @heapsize)
|
456
|
+
end
|
457
|
+
end
|
458
|
+
private :min_mode
|
459
|
+
|
460
|
+
def max_mode
|
461
|
+
if @mode != MaxHeap
|
462
|
+
@mode = MaxHeap
|
463
|
+
@heapsize = @mode.heapify(self, @ary)
|
464
|
+
elsif @heapsize < self.size
|
465
|
+
@heapsize = @mode.heapify(self, @ary, @heapsize)
|
466
|
+
end
|
467
|
+
end
|
468
|
+
private :max_mode
|
469
|
+
|
470
|
+
def mode_heapify
|
471
|
+
if @mode
|
472
|
+
@heapsize = @mode.heapify(self, @ary)
|
473
|
+
end
|
474
|
+
end
|
475
|
+
private :mode_heapify
|
476
|
+
|
477
|
+
def check_locator(loc)
|
478
|
+
if !self.equal?(loc.pdeque) ||
|
479
|
+
!get_entry(loc.send(:index))[0].equal?(loc)
|
480
|
+
raise ArgumentError, "unexpected locator"
|
481
|
+
end
|
482
|
+
end
|
483
|
+
private :check_locator
|
484
|
+
|
485
|
+
def default_subpriority
|
486
|
+
#return @subpriority_generator.call if @subpriority_generator
|
487
|
+
self.totalcount
|
488
|
+
end
|
489
|
+
private :default_subpriority
|
490
|
+
|
491
|
+
def compare_for_min(priority1, subpriority1, priority2, subpriority2)
|
492
|
+
compare_priority(priority1, priority2).nonzero? or
|
493
|
+
(subpriority1 <=> subpriority2)
|
494
|
+
end
|
495
|
+
private :compare_for_min
|
496
|
+
|
497
|
+
def compare_for_max(priority1, subpriority1, priority2, subpriority2)
|
498
|
+
compare_priority(priority1, priority2).nonzero? or
|
499
|
+
(subpriority2 <=> subpriority1)
|
500
|
+
end
|
501
|
+
private :compare_for_max
|
502
|
+
|
503
|
+
def initialize_copy(obj) # :nodoc:
|
504
|
+
if defined? @ary
|
505
|
+
@ary = @ary.dup
|
506
|
+
0.step(@ary.length-1, ARY_SLICE_SIZE) {|k|
|
507
|
+
i = k / ARY_SLICE_SIZE
|
508
|
+
loc1 = @ary[k]
|
509
|
+
priority = @ary[k+1]
|
510
|
+
loc2 = Depq::Locator.new(loc1.value, priority)
|
511
|
+
loc2.send(:internal_inserted, self, i)
|
512
|
+
@ary[k] = loc2
|
513
|
+
}
|
514
|
+
end
|
515
|
+
end
|
516
|
+
|
517
|
+
def inspect # :nodoc:
|
518
|
+
unless defined? @cmp
|
519
|
+
return "\#<#{self.class}: uninitialized>"
|
520
|
+
end
|
521
|
+
a = []
|
522
|
+
each_entry {|loc, priority|
|
523
|
+
value = loc.value
|
524
|
+
s = value.inspect
|
525
|
+
if value != priority
|
526
|
+
s << ":" << priority.inspect
|
527
|
+
end
|
528
|
+
a << s
|
529
|
+
}
|
530
|
+
"\#<#{self.class}: #{a.join(' ')}>"
|
531
|
+
end
|
532
|
+
|
533
|
+
def pretty_print(q) # :nodoc:
|
534
|
+
q.object_group(self) {
|
535
|
+
each_entry {|loc, priority|
|
536
|
+
q.breakable
|
537
|
+
value = loc.value
|
538
|
+
q.pp value
|
539
|
+
if value != priority
|
540
|
+
q.text ':'
|
541
|
+
q.pp priority
|
542
|
+
end
|
543
|
+
}
|
544
|
+
}
|
545
|
+
end
|
546
|
+
|
547
|
+
# compare priority1 and priority2.
|
548
|
+
#
|
549
|
+
# pd = Depq.new
|
550
|
+
# p pd.compare_priority("a", "b") #=> -1
|
551
|
+
# p pd.compare_priority("a", "a") #=> 0
|
552
|
+
# p pd.compare_priority("b", "a") #=> 1
|
553
|
+
#
|
554
|
+
def compare_priority(priority1, priority2)
|
555
|
+
if @cmp.kind_of? Symbol
|
556
|
+
priority1.__send__(@cmp, priority2)
|
557
|
+
else
|
558
|
+
@cmp.call(priority1, priority2)
|
559
|
+
end
|
560
|
+
end
|
561
|
+
|
562
|
+
# returns true if the queue is empty.
|
563
|
+
#
|
564
|
+
# pd = Depq.new
|
565
|
+
# p pd.empty? #=> true
|
566
|
+
# pd.insert 1
|
567
|
+
# p pd.empty? #=> false
|
568
|
+
# pd.delete_max
|
569
|
+
# p pd.empty? #=> true
|
570
|
+
#
|
571
|
+
def empty?
|
572
|
+
@ary.empty?
|
573
|
+
end
|
574
|
+
|
575
|
+
# returns the number of elements in the queue.
|
576
|
+
#
|
577
|
+
# pd = Depq.new
|
578
|
+
# p pd.size #=> 0
|
579
|
+
# pd.insert 1
|
580
|
+
# p pd.size #=> 1
|
581
|
+
# pd.insert 1
|
582
|
+
# p pd.size #=> 2
|
583
|
+
# pd.delete_min
|
584
|
+
# p pd.size #=> 1
|
585
|
+
# pd.delete_min
|
586
|
+
# p pd.size #=> 0
|
587
|
+
#
|
588
|
+
def size
|
589
|
+
@ary.size / ARY_SLICE_SIZE
|
590
|
+
end
|
591
|
+
alias length size
|
592
|
+
|
593
|
+
# returns the total number of elements inserted for the queue, ever.
|
594
|
+
#
|
595
|
+
# The result is monotonically increased.
|
596
|
+
#
|
597
|
+
# pd = Depq.new
|
598
|
+
# p [pd.size, pd.totalcount] #=> [0, 0]
|
599
|
+
# pd.insert 1
|
600
|
+
# p [pd.size, pd.totalcount] #=> [1, 1]
|
601
|
+
# pd.insert 2
|
602
|
+
# p [pd.size, pd.totalcount] #=> [2, 2]
|
603
|
+
# pd.delete_min
|
604
|
+
# p [pd.size, pd.totalcount] #=> [1, 2]
|
605
|
+
# pd.insert 4
|
606
|
+
# p [pd.size, pd.totalcount] #=> [2, 3]
|
607
|
+
# pd.insert 3
|
608
|
+
# p [pd.size, pd.totalcount] #=> [3, 4]
|
609
|
+
# pd.insert 0
|
610
|
+
# p [pd.size, pd.totalcount] #=> [4, 5]
|
611
|
+
# pd.delete_min
|
612
|
+
# p [pd.size, pd.totalcount] #=> [3, 5]
|
613
|
+
# pd.insert 2
|
614
|
+
# p [pd.size, pd.totalcount] #=> [4, 6]
|
615
|
+
#
|
616
|
+
def totalcount
|
617
|
+
@totalcount
|
618
|
+
end
|
619
|
+
|
620
|
+
# make the queue empty.
|
621
|
+
#
|
622
|
+
# Note that totalcount is not changed.
|
623
|
+
#
|
624
|
+
# pd = Depq.new
|
625
|
+
# pd.insert 1
|
626
|
+
# pd.insert 1
|
627
|
+
# p pd.size #=> 2
|
628
|
+
# p pd.totalcount #=> 2
|
629
|
+
# pd.clear
|
630
|
+
# p pd.size #=> 0
|
631
|
+
# p pd.totalcount #=> 2
|
632
|
+
# p pd.find_min #=> nil
|
633
|
+
#
|
634
|
+
def clear
|
635
|
+
@ary.clear
|
636
|
+
@heapsize = 0
|
637
|
+
@mode = nil
|
638
|
+
end
|
639
|
+
|
640
|
+
def internal_get_priority(loc)
|
641
|
+
check_locator(loc)
|
642
|
+
locator, priority, subpriority = get_entry(loc.send(:index))
|
643
|
+
[priority, subpriority]
|
644
|
+
end
|
645
|
+
private :internal_get_priority
|
646
|
+
|
647
|
+
def internal_set_priority(loc, priority, subpriority)
|
648
|
+
check_locator(loc)
|
649
|
+
if @heapsize <= loc.send(:index)
|
650
|
+
set_entry(loc.send(:index), loc, priority, subpriority)
|
651
|
+
else
|
652
|
+
mode_heapify
|
653
|
+
@mode.update_priority(self, @ary, loc, priority, subpriority)
|
654
|
+
end
|
655
|
+
end
|
656
|
+
private :internal_set_priority
|
657
|
+
|
658
|
+
# insert the locator to the queue.
|
659
|
+
#
|
660
|
+
# If loc.subpriority is nil, totalcount is used for stability.
|
661
|
+
#
|
662
|
+
# The locator should not already be inserted in a queue.
|
663
|
+
#
|
664
|
+
# pd = Depq.new
|
665
|
+
# loc = Depq::Locator.new(1)
|
666
|
+
# pd.insert_locator loc
|
667
|
+
# p pd.delete_min #=> 1
|
668
|
+
#
|
669
|
+
def insert_locator(loc)
|
670
|
+
subpriority = loc.subpriority || default_subpriority
|
671
|
+
i = self.size
|
672
|
+
priority = loc.send(:internal_inserted, self, i)
|
673
|
+
set_entry(i, loc, priority, subpriority)
|
674
|
+
@totalcount += 1
|
675
|
+
loc
|
676
|
+
end
|
677
|
+
|
678
|
+
# insert the value to the queue.
|
679
|
+
#
|
680
|
+
# If priority is omitted, the value itself is used as the priority.
|
681
|
+
#
|
682
|
+
# If subpriority is omitted or nil, totalcount is used for stability.
|
683
|
+
#
|
684
|
+
# pd = Depq.new
|
685
|
+
# pd.insert 3
|
686
|
+
# pd.insert 1
|
687
|
+
# pd.insert 2
|
688
|
+
# p pd.delete_min #=> 1
|
689
|
+
# p pd.delete_min #=> 2
|
690
|
+
# p pd.delete_min #=> 3
|
691
|
+
#
|
692
|
+
# pd = Depq.new
|
693
|
+
# pd.insert 3, 10
|
694
|
+
# pd.insert 1, 20
|
695
|
+
# pd.insert 2, 30
|
696
|
+
# p pd.delete_min #=> 3
|
697
|
+
# p pd.delete_min #=> 1
|
698
|
+
# p pd.delete_min #=> 2
|
699
|
+
#
|
700
|
+
# This method returns a locator which locates the inserted element.
|
701
|
+
# It can be used to update the value and priority, or delete the element.
|
702
|
+
#
|
703
|
+
# pd = Depq.new
|
704
|
+
# pd.insert 3
|
705
|
+
# loc1 = pd.insert 1
|
706
|
+
# loc2 = pd.insert 2
|
707
|
+
# pd.insert 4
|
708
|
+
# p pd.delete_max #=> 4
|
709
|
+
# pd.delete_locator loc1
|
710
|
+
# loc2.update 8
|
711
|
+
# p pd.delete_max #=> 8
|
712
|
+
# p pd.delete_max #=> 3
|
713
|
+
# p pd.delete_max #=> nil
|
714
|
+
#
|
715
|
+
def insert(value, priority=value, subpriority=nil)
|
716
|
+
loc = Locator.new(value, priority, subpriority)
|
717
|
+
insert_locator(loc)
|
718
|
+
end
|
719
|
+
alias add insert
|
720
|
+
alias put insert
|
721
|
+
alias enqueue insert
|
722
|
+
alias enq insert
|
723
|
+
alias << insert
|
724
|
+
|
725
|
+
# insert all values in iter.
|
726
|
+
#
|
727
|
+
# The argument, iter, should have each method.
|
728
|
+
#
|
729
|
+
# This method returns nil.
|
730
|
+
#
|
731
|
+
# pd = Depq.new
|
732
|
+
# pd.insert_all [3,1,2]
|
733
|
+
# p pd.delete_min #=> 1
|
734
|
+
# p pd.delete_min #=> 2
|
735
|
+
# p pd.delete_min #=> 3
|
736
|
+
# p pd.delete_min #=> nil
|
737
|
+
#
|
738
|
+
def insert_all(iter)
|
739
|
+
iter.each {|v|
|
740
|
+
insert v
|
741
|
+
}
|
742
|
+
nil
|
743
|
+
end
|
744
|
+
|
745
|
+
# return the locator for the minimum element.
|
746
|
+
# This method returns nil if the queue is empty.
|
747
|
+
#
|
748
|
+
# This method doesn't delete the element from the queue.
|
749
|
+
#
|
750
|
+
# pd = Depq.new
|
751
|
+
# p pd.find_min_locator #=> nil
|
752
|
+
# pd.insert 3
|
753
|
+
# pd.insert 1
|
754
|
+
# pd.insert 2
|
755
|
+
# p pd.find_min_locator #=> #<Depq::Locator: 1>
|
756
|
+
#
|
757
|
+
def find_min_locator
|
758
|
+
return nil if empty?
|
759
|
+
min_mode
|
760
|
+
@mode.find_min_locator(@ary)
|
761
|
+
end
|
762
|
+
|
763
|
+
# return the minimum value with its priority.
|
764
|
+
# This method returns nil if the queue is empty.
|
765
|
+
#
|
766
|
+
# This method doesn't delete the element from the queue.
|
767
|
+
#
|
768
|
+
# pd = Depq.new
|
769
|
+
# p pd.find_min_priority #=> nil
|
770
|
+
# pd.insert "durian", 1
|
771
|
+
# pd.insert "banana", 3
|
772
|
+
# pd.insert "melon", 2
|
773
|
+
# p pd.find_min_priority #=> ["durian", 1]
|
774
|
+
# pd.clear
|
775
|
+
# p pd.find_min_priority #=> nil
|
776
|
+
#
|
777
|
+
def find_min_priority
|
778
|
+
loc = find_min_locator and [loc.value, loc.priority]
|
779
|
+
end
|
780
|
+
|
781
|
+
# return the minimum value.
|
782
|
+
# This method returns nil if the queue is empty.
|
783
|
+
#
|
784
|
+
# This method doesn't delete the element from the queue.
|
785
|
+
#
|
786
|
+
# pd = Depq.new
|
787
|
+
# p pd.find_min #=> nil
|
788
|
+
# pd.insert 3
|
789
|
+
# pd.insert 1
|
790
|
+
# pd.insert 2
|
791
|
+
# p pd.find_min #=> 1
|
792
|
+
#
|
793
|
+
def find_min
|
794
|
+
loc = find_min_locator and loc.value
|
795
|
+
end
|
796
|
+
alias min find_min
|
797
|
+
alias first find_min
|
798
|
+
|
799
|
+
# return the locator for the maximum element.
|
800
|
+
# This method returns nil if the queue is empty.
|
801
|
+
#
|
802
|
+
# This method doesn't delete the element from the queue.
|
803
|
+
#
|
804
|
+
# pd = Depq.new
|
805
|
+
# p pd.find_max_locator #=> nil
|
806
|
+
# pd.insert 3
|
807
|
+
# pd.insert 1
|
808
|
+
# pd.insert 2
|
809
|
+
# p pd.find_max_locator #=> #<Depq::Locator: 3>
|
810
|
+
#
|
811
|
+
def find_max_locator
|
812
|
+
return nil if empty?
|
813
|
+
max_mode
|
814
|
+
@mode.find_max_locator(@ary)
|
815
|
+
end
|
816
|
+
|
817
|
+
# return the maximum value with its priority.
|
818
|
+
# This method returns nil if the queue is empty.
|
819
|
+
#
|
820
|
+
# This method doesn't delete the element from the queue.
|
821
|
+
#
|
822
|
+
# pd = Depq.new
|
823
|
+
# p pd.find_max_priority #=> nil
|
824
|
+
# pd.insert "durian", 1
|
825
|
+
# pd.insert "banana", 3
|
826
|
+
# pd.insert "melon", 2
|
827
|
+
# p pd.find_max_priority #=> ["banana", 3]
|
828
|
+
# pd.clear
|
829
|
+
# p pd.find_max_priority #=> nil
|
830
|
+
#
|
831
|
+
def find_max_priority
|
832
|
+
loc = find_max_locator and [loc.value, loc.priority]
|
833
|
+
end
|
834
|
+
|
835
|
+
# returns the maximum value.
|
836
|
+
# This method returns nil if the queue is empty.
|
837
|
+
#
|
838
|
+
# This method doesn't delete the element from the queue.
|
839
|
+
#
|
840
|
+
# pd = Depq.new
|
841
|
+
# p pd.find_max #=> nil
|
842
|
+
# pd.insert 3
|
843
|
+
# pd.insert 1
|
844
|
+
# pd.insert 2
|
845
|
+
# p pd.find_max #=> 3
|
846
|
+
#
|
847
|
+
def find_max
|
848
|
+
loc = find_max_locator and loc.value
|
849
|
+
end
|
850
|
+
alias max find_max
|
851
|
+
alias last find_max
|
852
|
+
|
853
|
+
# returns the locators for the minimum and maximum element as a two-element array.
|
854
|
+
# If the queue is empty, [nil, nil] is returned.
|
855
|
+
#
|
856
|
+
# pd = Depq.new
|
857
|
+
# p pd.find_minmax_locator #=> [nil, nil]
|
858
|
+
# pd.insert 3
|
859
|
+
# pd.insert 1
|
860
|
+
# pd.insert 2
|
861
|
+
# p pd.find_minmax_locator #=> [#<Depq::Locator: 1>, #<Depq::Locator: 3>]
|
862
|
+
#
|
863
|
+
def find_minmax_locator
|
864
|
+
return [nil, nil] if empty?
|
865
|
+
case @mode
|
866
|
+
when :min
|
867
|
+
loc1 = find_min_locator
|
868
|
+
loc2 = loc1
|
869
|
+
self.each_locator {|loc|
|
870
|
+
if compare_for_max(loc2.priority, loc2.subpriority, loc.priority, loc.subpriority) < 0
|
871
|
+
loc2 = loc
|
872
|
+
end
|
873
|
+
}
|
874
|
+
when :max
|
875
|
+
loc2 = find_max_locator
|
876
|
+
loc1 = loc2
|
877
|
+
self.each_locator {|loc|
|
878
|
+
if compare_for_min(loc1.priority, loc1.subpriority, loc.priority, loc.subpriority) > 0
|
879
|
+
loc1 = loc
|
880
|
+
end
|
881
|
+
}
|
882
|
+
else
|
883
|
+
loc1 = loc2 = nil
|
884
|
+
self.each_locator {|loc|
|
885
|
+
if loc1 == nil || compare_for_min(loc1.priority, loc1.subpriority, loc.priority, loc.subpriority) > 0
|
886
|
+
loc1 = loc
|
887
|
+
end
|
888
|
+
if loc2 == nil || compare_for_max(loc2.priority, loc2.subpriority, loc.priority, loc.subpriority) < 0
|
889
|
+
loc2 = loc
|
890
|
+
end
|
891
|
+
}
|
892
|
+
end
|
893
|
+
[loc1, loc2]
|
894
|
+
end
|
895
|
+
|
896
|
+
# returns the minimum and maximum value as a two-element array.
|
897
|
+
# If the queue is empty, [nil, nil] is returned.
|
898
|
+
#
|
899
|
+
# pd = Depq.new
|
900
|
+
# p pd.find_minmax #=> [nil, nil]
|
901
|
+
# pd.insert 3
|
902
|
+
# pd.insert 1
|
903
|
+
# pd.insert 2
|
904
|
+
# p pd.find_minmax #=> [1, 3]
|
905
|
+
#
|
906
|
+
def find_minmax
|
907
|
+
loc1, loc2 = self.find_minmax_locator
|
908
|
+
[loc1 && loc1.value, loc2 && loc2.value]
|
909
|
+
end
|
910
|
+
alias minmax find_minmax
|
911
|
+
|
912
|
+
# delete the element specified by the locator.
|
913
|
+
#
|
914
|
+
# pd = Depq.new
|
915
|
+
# pd.insert 3
|
916
|
+
# loc = pd.insert 2
|
917
|
+
# pd.insert 1
|
918
|
+
# pd.delete_locator loc
|
919
|
+
# p pd.delete_min #=> 1
|
920
|
+
# p pd.delete_min #=> 3
|
921
|
+
# p pd.delete_min #=> nil
|
922
|
+
#
|
923
|
+
def delete_locator(loc)
|
924
|
+
check_locator(loc)
|
925
|
+
if @heapsize <= loc.send(:index)
|
926
|
+
_, priority, subpriority = delete_entry(loc.send(:index))
|
927
|
+
loc.send(:index).upto(self.size-1) {|i|
|
928
|
+
loc2, _ = get_entry(i)
|
929
|
+
loc2.send(:index=, i)
|
930
|
+
}
|
931
|
+
loc.send(:internal_deleted, priority, subpriority)
|
932
|
+
loc
|
933
|
+
else
|
934
|
+
mode_heapify
|
935
|
+
@heapsize = @mode.delete_locator(self, @ary, loc)
|
936
|
+
loc
|
937
|
+
end
|
938
|
+
end
|
939
|
+
|
940
|
+
# delete the minimum element in the queue and returns the locator.
|
941
|
+
#
|
942
|
+
# This method returns the locator for the deleted element.
|
943
|
+
# nil is returned if the queue is empty.
|
944
|
+
#
|
945
|
+
# pd = Depq.new
|
946
|
+
# pd.insert 2
|
947
|
+
# pd.insert 1
|
948
|
+
# pd.insert 3
|
949
|
+
# p pd.delete_min_locator #=> #<Depq::Locator: 1 (no queue)>
|
950
|
+
# p pd.delete_min_locator #=> #<Depq::Locator: 2 (no queue)>
|
951
|
+
# p pd.delete_min_locator #=> #<Depq::Locator: 3 (no queue)>
|
952
|
+
# p pd.delete_min_locator #=> nil
|
953
|
+
#
|
954
|
+
def delete_min_locator
|
955
|
+
return nil if empty?
|
956
|
+
min_mode
|
957
|
+
loc = @mode.find_min_locator(@ary)
|
958
|
+
@heapsize = @mode.delete_locator(self, @ary, loc)
|
959
|
+
loc
|
960
|
+
end
|
961
|
+
|
962
|
+
# delete the minimum element in the queue and returns the value and its priority.
|
963
|
+
#
|
964
|
+
# This method returns an array which contains the value and its priority
|
965
|
+
# of the deleted element.
|
966
|
+
# nil is returned if the queue is empty.
|
967
|
+
#
|
968
|
+
# pd = Depq.new
|
969
|
+
# pd.insert "durian", 1
|
970
|
+
# pd.insert "banana", 3
|
971
|
+
# pd.insert "melon", 2
|
972
|
+
# p pd.delete_min_priority #=> ["durian", 1]
|
973
|
+
# p pd.delete_min_priority #=> ["melon", 2]
|
974
|
+
# p pd.delete_min_priority #=> ["banana", 3]
|
975
|
+
# p pd.delete_min_priority #=> nil
|
976
|
+
#
|
977
|
+
def delete_min_priority
|
978
|
+
loc = delete_min_locator
|
979
|
+
return nil unless loc
|
980
|
+
[loc.value, loc.priority]
|
981
|
+
end
|
982
|
+
|
983
|
+
# delete the minimum element in the queue and returns the value.
|
984
|
+
#
|
985
|
+
# This method returns the value of the deleted element.
|
986
|
+
# nil is returned if the queue is empty.
|
987
|
+
#
|
988
|
+
# pd = Depq.new
|
989
|
+
# pd.insert 3
|
990
|
+
# pd.insert 1
|
991
|
+
# pd.insert 2
|
992
|
+
# p pd.delete_min #=> 1
|
993
|
+
# p pd.delete_min #=> 2
|
994
|
+
# p pd.delete_min #=> 3
|
995
|
+
# p pd.delete_min #=> nil
|
996
|
+
#
|
997
|
+
def delete_min
|
998
|
+
loc = delete_min_locator
|
999
|
+
loc and loc.value
|
1000
|
+
end
|
1001
|
+
alias shift delete_min
|
1002
|
+
alias dequeue delete_min
|
1003
|
+
alias deq delete_min
|
1004
|
+
|
1005
|
+
# delete the maximum element in the queue and returns the locator.
|
1006
|
+
#
|
1007
|
+
# This method returns the locator for the deleted element.
|
1008
|
+
# nil is returned if the queue is empty.
|
1009
|
+
#
|
1010
|
+
# pd = Depq.new
|
1011
|
+
# pd.insert 2
|
1012
|
+
# pd.insert 1
|
1013
|
+
# pd.insert 3
|
1014
|
+
# p pd.delete_max_locator #=> #<Depq::Locator: 3 (no queue)>
|
1015
|
+
# p pd.delete_max_locator #=> #<Depq::Locator: 2 (no queue)>
|
1016
|
+
# p pd.delete_max_locator #=> #<Depq::Locator: 1 (no queue)>
|
1017
|
+
# p pd.delete_max_locator #=> nil
|
1018
|
+
#
|
1019
|
+
def delete_max_locator
|
1020
|
+
return nil if empty?
|
1021
|
+
max_mode
|
1022
|
+
loc = @mode.find_max_locator(@ary)
|
1023
|
+
@heapsize = @mode.delete_locator(self, @ary, loc)
|
1024
|
+
loc
|
1025
|
+
end
|
1026
|
+
|
1027
|
+
# delete the maximum element in the queue and returns the value and its priority.
|
1028
|
+
#
|
1029
|
+
# This method returns an array which contains the value and its priority
|
1030
|
+
# of the deleted element.
|
1031
|
+
# nil is returned if the queue is empty.
|
1032
|
+
#
|
1033
|
+
# pd = Depq.new
|
1034
|
+
# pd.insert "durian", 1
|
1035
|
+
# pd.insert "banana", 3
|
1036
|
+
# pd.insert "melon", 2
|
1037
|
+
# p pd.delete_max_priority #=> ["banana", 3]
|
1038
|
+
# p pd.delete_max_priority #=> ["melon", 2]
|
1039
|
+
# p pd.delete_max_priority #=> ["durian", 1]
|
1040
|
+
# p pd.delete_max_priority #=> nil
|
1041
|
+
#
|
1042
|
+
def delete_max_priority
|
1043
|
+
loc = delete_max_locator
|
1044
|
+
return nil unless loc
|
1045
|
+
[loc.value, loc.priority]
|
1046
|
+
end
|
1047
|
+
|
1048
|
+
# delete the maximum element in the queue and returns the value.
|
1049
|
+
#
|
1050
|
+
# This method returns the value of the deleted element.
|
1051
|
+
# nil is returned if the queue is empty.
|
1052
|
+
#
|
1053
|
+
# pd = Depq.new
|
1054
|
+
# pd.insert 3
|
1055
|
+
# pd.insert 1
|
1056
|
+
# pd.insert 2
|
1057
|
+
# p pd.delete_max #=> 3
|
1058
|
+
# p pd.delete_max #=> 2
|
1059
|
+
# p pd.delete_max #=> 1
|
1060
|
+
# p pd.delete_max #=> nil
|
1061
|
+
#
|
1062
|
+
def delete_max
|
1063
|
+
loc = delete_max_locator
|
1064
|
+
loc and loc.value
|
1065
|
+
end
|
1066
|
+
alias pop delete_max
|
1067
|
+
|
1068
|
+
# delete an element in the queue and returns the locator.
|
1069
|
+
# The element is choosen for fast deletion.
|
1070
|
+
#
|
1071
|
+
# This method returns the locator for the deleted element.
|
1072
|
+
# nil is returned if the queue is empty.
|
1073
|
+
#
|
1074
|
+
# pd = Depq.new
|
1075
|
+
# pd.insert 1
|
1076
|
+
# pd.insert 4
|
1077
|
+
# pd.insert 3
|
1078
|
+
# p pd.delete_unspecified_locator #=> #<Depq::Locator: 3 (no queue)>
|
1079
|
+
# p pd.delete_unspecified_locator #=> #<Depq::Locator: 4 (no queue)>
|
1080
|
+
# p pd.delete_unspecified_locator #=> #<Depq::Locator: 1 (no queue)>
|
1081
|
+
# p pd.delete_unspecified_locator #=> nil
|
1082
|
+
#
|
1083
|
+
def delete_unspecified_locator
|
1084
|
+
return nil if empty?
|
1085
|
+
loc, _ = get_entry(self.size-1)
|
1086
|
+
delete_locator(loc)
|
1087
|
+
end
|
1088
|
+
|
1089
|
+
# delete an element in the queue and returns the value and its priority.
|
1090
|
+
# The element is choosen for fast deletion.
|
1091
|
+
#
|
1092
|
+
# This method returns an array which contains the value and its priority
|
1093
|
+
# of the deleted element.
|
1094
|
+
# nil is returned if the queue is empty.
|
1095
|
+
#
|
1096
|
+
# pd = Depq.new
|
1097
|
+
# pd.insert "durian", 1
|
1098
|
+
# pd.insert "banana", 3
|
1099
|
+
# pd.insert "melon", 2
|
1100
|
+
# p pd.delete_unspecified_priority #=> ["melon", 2]
|
1101
|
+
# p pd.delete_unspecified_priority #=> ["banana", 3]
|
1102
|
+
# p pd.delete_unspecified_priority #=> ["durian", 1]
|
1103
|
+
# p pd.delete_unspecified_priority #=> nil
|
1104
|
+
#
|
1105
|
+
def delete_unspecified_priority
|
1106
|
+
loc = delete_unspecified_locator
|
1107
|
+
return nil unless loc
|
1108
|
+
[loc.value, loc.priority]
|
1109
|
+
end
|
1110
|
+
|
1111
|
+
# delete an element in the queue and returns the value.
|
1112
|
+
# The element is choosen for fast deletion.
|
1113
|
+
#
|
1114
|
+
# This method returns the value of the deleted element.
|
1115
|
+
# nil is returned if the queue is empty.
|
1116
|
+
#
|
1117
|
+
# pd = Depq.new
|
1118
|
+
# pd.insert 1
|
1119
|
+
# pd.insert 4
|
1120
|
+
# pd.insert 3
|
1121
|
+
# p pd.delete_unspecified #=> 3
|
1122
|
+
# p pd.delete_unspecified #=> 4
|
1123
|
+
# p pd.delete_unspecified #=> 1
|
1124
|
+
# p pd.delete_unspecified #=> nil
|
1125
|
+
#
|
1126
|
+
def delete_unspecified
|
1127
|
+
loc = delete_unspecified_locator
|
1128
|
+
return nil unless loc
|
1129
|
+
loc.value
|
1130
|
+
end
|
1131
|
+
|
1132
|
+
# iterate over the locators in the queue.
|
1133
|
+
#
|
1134
|
+
# The iteration order is unspecified.
|
1135
|
+
#
|
1136
|
+
# pd = Depq.new
|
1137
|
+
# pd.insert 3
|
1138
|
+
# pd.insert 1
|
1139
|
+
# pd.insert 2
|
1140
|
+
# p pd.delete_min #=> 1
|
1141
|
+
# pd.each_locator {|v|
|
1142
|
+
# p v #=> #<Depq::Locator: 2>, #<Depq::Locator: 3>
|
1143
|
+
# }
|
1144
|
+
#
|
1145
|
+
def each_locator # :yield: locator
|
1146
|
+
each_entry {|locator, priority|
|
1147
|
+
yield locator
|
1148
|
+
}
|
1149
|
+
nil
|
1150
|
+
end
|
1151
|
+
|
1152
|
+
# iterate over the values and priorities in the queue.
|
1153
|
+
#
|
1154
|
+
# pd = Depq.new
|
1155
|
+
# pd.insert "durian", 1
|
1156
|
+
# pd.insert "banana", 3
|
1157
|
+
# pd.insert "melon", 2
|
1158
|
+
# pd.each_with_priority {|val, priority|
|
1159
|
+
# p [val, priority]
|
1160
|
+
# }
|
1161
|
+
# #=> ["durian", 1]
|
1162
|
+
# # ["banana", 3]
|
1163
|
+
# # ["melon", 2]
|
1164
|
+
#
|
1165
|
+
def each_with_priority # :yield: value, priority
|
1166
|
+
each_entry {|locator, priority|
|
1167
|
+
yield locator.value, priority
|
1168
|
+
}
|
1169
|
+
nil
|
1170
|
+
end
|
1171
|
+
|
1172
|
+
# iterate over the values in the queue.
|
1173
|
+
#
|
1174
|
+
# The iteration order is unspecified.
|
1175
|
+
#
|
1176
|
+
# pd = Depq.new
|
1177
|
+
# pd.insert 3
|
1178
|
+
# pd.insert 1
|
1179
|
+
# pd.insert 2
|
1180
|
+
# p pd.delete_min #=> 1
|
1181
|
+
# pd.each {|v|
|
1182
|
+
# p v #=> 2, 3
|
1183
|
+
# }
|
1184
|
+
#
|
1185
|
+
def each # :yield: value
|
1186
|
+
each_entry {|locator, priority|
|
1187
|
+
yield locator.value
|
1188
|
+
}
|
1189
|
+
nil
|
1190
|
+
end
|
1191
|
+
|
1192
|
+
# returns the largest n elements in iter as an array.
|
1193
|
+
#
|
1194
|
+
# The result array is ordered from the minimum element.
|
1195
|
+
#
|
1196
|
+
# p Depq.nlargest(3, [5, 2, 3, 1, 4, 6, 7]) #=> [5, 6, 7]
|
1197
|
+
#
|
1198
|
+
def Depq.nlargest(n, iter)
|
1199
|
+
limit = (n * Math.log(1+n)).ceil
|
1200
|
+
limit = 1024 if limit < 1024
|
1201
|
+
pd = Depq.new
|
1202
|
+
threshold = nil
|
1203
|
+
iter.each {|v|
|
1204
|
+
if pd.size < n
|
1205
|
+
if pd.size == 0
|
1206
|
+
threshold = v
|
1207
|
+
else
|
1208
|
+
threshold = v if (v <=> threshold) < 0
|
1209
|
+
end
|
1210
|
+
pd.insert v
|
1211
|
+
else
|
1212
|
+
if (v <=> threshold) > 0
|
1213
|
+
pd.insert v
|
1214
|
+
if limit < pd.size
|
1215
|
+
tmp = []
|
1216
|
+
n.times { tmp << pd.delete_max }
|
1217
|
+
pd.clear
|
1218
|
+
pd.insert_all tmp
|
1219
|
+
threshold = tmp.last
|
1220
|
+
end
|
1221
|
+
end
|
1222
|
+
end
|
1223
|
+
}
|
1224
|
+
n = pd.size if pd.size < n
|
1225
|
+
a = []
|
1226
|
+
n.times { a << pd.delete_max }
|
1227
|
+
a.reverse!
|
1228
|
+
a
|
1229
|
+
end
|
1230
|
+
|
1231
|
+
# returns the smallest n elements in iter as an array.
|
1232
|
+
#
|
1233
|
+
# The result array is ordered from the minimum element.
|
1234
|
+
#
|
1235
|
+
# p Depq.nsmallest(5, [5, 2, 3, 1, 4, 6, 7]) #=> [1, 2, 3, 4, 5]
|
1236
|
+
#
|
1237
|
+
def Depq.nsmallest(n, iter)
|
1238
|
+
limit = (n * Math.log(1+n)).ceil
|
1239
|
+
limit = 1024 if limit < 1024
|
1240
|
+
pd = Depq.new
|
1241
|
+
threshold = nil
|
1242
|
+
iter.each {|v|
|
1243
|
+
if pd.size < n
|
1244
|
+
if pd.size == 0
|
1245
|
+
threshold = v
|
1246
|
+
else
|
1247
|
+
threshold = v if (v <=> threshold) > 0
|
1248
|
+
end
|
1249
|
+
pd.insert v
|
1250
|
+
else
|
1251
|
+
if (v <=> threshold) < 0
|
1252
|
+
pd.insert v
|
1253
|
+
if limit < pd.size
|
1254
|
+
tmp = []
|
1255
|
+
n.times { tmp << pd.delete_min }
|
1256
|
+
pd.clear
|
1257
|
+
pd.insert_all tmp
|
1258
|
+
threshold = tmp.last
|
1259
|
+
end
|
1260
|
+
end
|
1261
|
+
end
|
1262
|
+
}
|
1263
|
+
n = pd.size if pd.size < n
|
1264
|
+
a = []
|
1265
|
+
n.times {
|
1266
|
+
a << pd.delete_min
|
1267
|
+
}
|
1268
|
+
a
|
1269
|
+
end
|
1270
|
+
|
1271
|
+
# iterates over iterators specified by arguments.
|
1272
|
+
#
|
1273
|
+
# The iteration order is sorted, from minimum to maximum,
|
1274
|
+
# if all the arugment iterators are sorted.
|
1275
|
+
#
|
1276
|
+
# Depq.merge(1..4, 3..6) {|v| p v }
|
1277
|
+
# #=> 1
|
1278
|
+
# # 2
|
1279
|
+
# # 3
|
1280
|
+
# # 3
|
1281
|
+
# # 4
|
1282
|
+
# # 4
|
1283
|
+
# # 5
|
1284
|
+
# # 6
|
1285
|
+
#
|
1286
|
+
def Depq.merge(*iters, &b)
|
1287
|
+
pd = Depq.new
|
1288
|
+
iters.each {|enum|
|
1289
|
+
enum = enum.to_enum unless enum.kind_of? Enumerator
|
1290
|
+
begin
|
1291
|
+
val = enum.next
|
1292
|
+
rescue StopIteration
|
1293
|
+
next
|
1294
|
+
end
|
1295
|
+
pd.insert enum, val
|
1296
|
+
}
|
1297
|
+
loop = lambda {|y, meth|
|
1298
|
+
until pd.empty?
|
1299
|
+
loc = pd.find_min_locator
|
1300
|
+
enum = loc.value
|
1301
|
+
val = loc.priority
|
1302
|
+
y.send meth, val
|
1303
|
+
begin
|
1304
|
+
val = enum.next
|
1305
|
+
rescue StopIteration
|
1306
|
+
pd.delete_locator loc
|
1307
|
+
next
|
1308
|
+
end
|
1309
|
+
loc.update enum, val
|
1310
|
+
end
|
1311
|
+
}
|
1312
|
+
if block_given?
|
1313
|
+
loop.call(b, :call)
|
1314
|
+
else
|
1315
|
+
Enumerator.new {|y|
|
1316
|
+
loop.call(y, :yield)
|
1317
|
+
}
|
1318
|
+
end
|
1319
|
+
end
|
1320
|
+
|
1321
|
+
# :stopdoc:
|
1322
|
+
|
1323
|
+
module SimpleHeap
|
1324
|
+
|
1325
|
+
def size(ary)
|
1326
|
+
return ary.size / ARY_SLICE_SIZE
|
1327
|
+
end
|
1328
|
+
|
1329
|
+
def get_entry(ary, i)
|
1330
|
+
locator = ary[i*ARY_SLICE_SIZE+0]
|
1331
|
+
priority = ary[i*ARY_SLICE_SIZE+1]
|
1332
|
+
subpriority = ary[i*ARY_SLICE_SIZE+2]
|
1333
|
+
[locator, priority, subpriority]
|
1334
|
+
end
|
1335
|
+
|
1336
|
+
def set_entry(ary, i, locator, priority, subpriority)
|
1337
|
+
tmp = Array.new(ARY_SLICE_SIZE)
|
1338
|
+
tmp[0] = locator
|
1339
|
+
tmp[1] = priority
|
1340
|
+
tmp[2] = subpriority
|
1341
|
+
ary[i*ARY_SLICE_SIZE, ARY_SLICE_SIZE] = tmp
|
1342
|
+
end
|
1343
|
+
|
1344
|
+
def delete_entry(ary, i)
|
1345
|
+
locator, priority, subpriority = ary[i*ARY_SLICE_SIZE, ARY_SLICE_SIZE]
|
1346
|
+
ary[i*ARY_SLICE_SIZE, ARY_SLICE_SIZE] = []
|
1347
|
+
[locator, priority, subpriority]
|
1348
|
+
end
|
1349
|
+
|
1350
|
+
def each_entry(ary)
|
1351
|
+
0.upto(self.size-1) {|i|
|
1352
|
+
ei = ary[i*ARY_SLICE_SIZE+0]
|
1353
|
+
pi = ary[i*ARY_SLICE_SIZE+1]
|
1354
|
+
si = ary[i*ARY_SLICE_SIZE+2]
|
1355
|
+
yield ei, pi, si
|
1356
|
+
}
|
1357
|
+
end
|
1358
|
+
|
1359
|
+
def swap(ary, i, j)
|
1360
|
+
ei, pi, si = get_entry(ary, i)
|
1361
|
+
ej, pj, sj = get_entry(ary, j)
|
1362
|
+
set_entry(ary, i, ej, pj, sj)
|
1363
|
+
set_entry(ary, j, ei, pi, si)
|
1364
|
+
ei.send(:index=, j)
|
1365
|
+
ej.send(:index=, i)
|
1366
|
+
end
|
1367
|
+
|
1368
|
+
def upheap(pd, ary, j)
|
1369
|
+
while true
|
1370
|
+
return if j <= 0
|
1371
|
+
i = (j-1) >> 1
|
1372
|
+
return if upper?(pd, ary, i, j)
|
1373
|
+
swap(ary, j, i)
|
1374
|
+
j = i
|
1375
|
+
end
|
1376
|
+
end
|
1377
|
+
|
1378
|
+
def downheap(pd, ary, i)
|
1379
|
+
while true
|
1380
|
+
j = i*2+1
|
1381
|
+
k = i*2+2
|
1382
|
+
return if size(ary) <= j
|
1383
|
+
if size(ary) == k
|
1384
|
+
return if upper?(pd, ary, i, j)
|
1385
|
+
swap(ary, i, j)
|
1386
|
+
i = j
|
1387
|
+
else
|
1388
|
+
return if upper?(pd, ary, i, j) && upper?(pd, ary, i, k)
|
1389
|
+
loc = upper?(pd, ary, j, k) ? j : k
|
1390
|
+
swap(ary, i, loc)
|
1391
|
+
i = loc
|
1392
|
+
end
|
1393
|
+
end
|
1394
|
+
end
|
1395
|
+
|
1396
|
+
def find_top_locator(ary)
|
1397
|
+
loc, _ = get_entry(ary, 0)
|
1398
|
+
loc
|
1399
|
+
end
|
1400
|
+
|
1401
|
+
def delete_locator(pd, ary, loc)
|
1402
|
+
i = loc.send(:index)
|
1403
|
+
_, priority, subpriority = get_entry(ary, i)
|
1404
|
+
last = size(ary) - 1
|
1405
|
+
loc.send(:internal_deleted, priority, subpriority)
|
1406
|
+
el, pl, sl = delete_entry(ary, last)
|
1407
|
+
if i != last
|
1408
|
+
set_entry(ary, i, el, pl, sl)
|
1409
|
+
el.send(:index=, i)
|
1410
|
+
downheap(pd, ary, i)
|
1411
|
+
end
|
1412
|
+
size(ary)
|
1413
|
+
end
|
1414
|
+
|
1415
|
+
def heapify(pd, ary, heapsize=0)
|
1416
|
+
# compare number of data movements in worst case.
|
1417
|
+
# choose a way for less data movements.
|
1418
|
+
#
|
1419
|
+
# current size = ary.size / ARY_SLICE_SIZE = n
|
1420
|
+
# addition size = n - heapsize = m
|
1421
|
+
# heap tree height = Math.log2(n+1) = h
|
1422
|
+
#
|
1423
|
+
# worst data movements using downheap:
|
1424
|
+
# - bottom elements cannot move.
|
1425
|
+
# - elements above can move 1 time.
|
1426
|
+
# - ...
|
1427
|
+
# - top element can move h-1 times.
|
1428
|
+
#
|
1429
|
+
# 1*2**(h-2) + 2*2**(h-3) + 3*2**(h-4) + ... + (h-1)*2**0
|
1430
|
+
# = sum i*2**(h-1-i), i=1 to h-1
|
1431
|
+
# = 2**h - h - 1
|
1432
|
+
# = n - h
|
1433
|
+
#
|
1434
|
+
# worst data movements using upheap
|
1435
|
+
# - bottom elements can move h-1 times.
|
1436
|
+
# - n/2 elements are placed at bottom.
|
1437
|
+
# - approximate all elements are at bottom...
|
1438
|
+
#
|
1439
|
+
# (h-1) * m
|
1440
|
+
#
|
1441
|
+
# condition to use downheap:
|
1442
|
+
#
|
1443
|
+
# n - h < (h-1) * m
|
1444
|
+
# n - 1 < (h-1) * (m+1)
|
1445
|
+
#
|
1446
|
+
currentsize = size(ary)
|
1447
|
+
h = Math.log(currentsize+1)/Math.log(2)
|
1448
|
+
if currentsize - 1 < (h - 1) * (currentsize - heapsize + 1)
|
1449
|
+
n = (currentsize - 2) / 2
|
1450
|
+
n.downto(0) {|i|
|
1451
|
+
downheap(pd, ary, i)
|
1452
|
+
}
|
1453
|
+
else
|
1454
|
+
heapsize.upto(currentsize-1) {|i|
|
1455
|
+
upheap(pd, ary, i)
|
1456
|
+
}
|
1457
|
+
end
|
1458
|
+
currentsize
|
1459
|
+
end
|
1460
|
+
end
|
1461
|
+
|
1462
|
+
module MinHeap
|
1463
|
+
end
|
1464
|
+
class << MinHeap
|
1465
|
+
include SimpleHeap
|
1466
|
+
|
1467
|
+
def upper?(pd, ary, i, j)
|
1468
|
+
ei, pi, si = get_entry(ary, i)
|
1469
|
+
ej, pj, sj = get_entry(ary, j)
|
1470
|
+
pd.send(:compare_for_min, pi, si, pj, sj) <= 0
|
1471
|
+
end
|
1472
|
+
|
1473
|
+
def update_priority(pd, ary, loc, priority, subpriority)
|
1474
|
+
i = loc.send(:index)
|
1475
|
+
ei, pi, si = get_entry(ary, i)
|
1476
|
+
cmp = pd.send(:compare_for_min, pi, si, priority, subpriority)
|
1477
|
+
set_entry(ary, i, ei, priority, subpriority)
|
1478
|
+
if cmp < 0
|
1479
|
+
# loc.priority < priority
|
1480
|
+
downheap(pd, ary, i)
|
1481
|
+
elsif cmp > 0
|
1482
|
+
# loc.priority > priority
|
1483
|
+
upheap(pd, ary, i)
|
1484
|
+
end
|
1485
|
+
end
|
1486
|
+
|
1487
|
+
alias find_min_locator find_top_locator
|
1488
|
+
end
|
1489
|
+
|
1490
|
+
module MaxHeap
|
1491
|
+
end
|
1492
|
+
class << MaxHeap
|
1493
|
+
include SimpleHeap
|
1494
|
+
|
1495
|
+
def upper?(pd, ary, i, j)
|
1496
|
+
ei, pi, si = get_entry(ary, i)
|
1497
|
+
ej, pj, sj = get_entry(ary, j)
|
1498
|
+
pd.send(:compare_for_max, pi, si, pj, sj) >= 0
|
1499
|
+
end
|
1500
|
+
|
1501
|
+
def update_priority(pd, ary, loc, priority, subpriority)
|
1502
|
+
i = loc.send(:index)
|
1503
|
+
ei, pi, si = get_entry(ary, i)
|
1504
|
+
subpriority ||= si
|
1505
|
+
cmp = pd.send(:compare_for_max, pi, si, priority, subpriority)
|
1506
|
+
set_entry(ary, i, ei, priority, subpriority)
|
1507
|
+
if cmp < 0
|
1508
|
+
# loc.priority < priority
|
1509
|
+
upheap(pd, ary, i)
|
1510
|
+
elsif cmp > 0
|
1511
|
+
# loc.priority > priority
|
1512
|
+
downheap(pd, ary, i)
|
1513
|
+
end
|
1514
|
+
end
|
1515
|
+
|
1516
|
+
alias find_max_locator find_top_locator
|
1517
|
+
end
|
1518
|
+
|
1519
|
+
# :startdoc:
|
1520
|
+
end
|