-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathPriorityQueue.lua
232 lines (195 loc) · 5.82 KB
/
PriorityQueue.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
--[[
PriorityQueue - v1.0.1 - public domain Lua priority queue
implemented with indirect binary heap
no warranty implied; use at your own risk
based on binaryheap library (github.com/iskolbin/binaryheap)
author: Ilya Kolbin ([email protected])
url: github.com/iskolbin/priorityqueue
See documentation in README file.
COMPATIBILITY
Lua 5.1, 5.2, 5.3, LuaJIT 1, 2
LICENSE
This software is dual-licensed to the public domain and under the following
license: you are granted a perpetual, irrevocable license to copy, modify,
publish, and distribute this file as you see fit.
--]]
local floor, setmetatable = math.floor, setmetatable
local function siftup( self, from )
local items, priorities, indices, higherpriority = self, self._priorities, self._indices, self._higherpriority
local index = from
local parent = floor( index / 2 )
while index > 1 and higherpriority( priorities[index], priorities[parent] ) do
priorities[index], priorities[parent] = priorities[parent], priorities[index]
items[index], items[parent] = items[parent], items[index]
indices[items[index]], indices[items[parent]] = index, parent
index = parent
parent = floor( index / 2 )
end
return index
end
local function siftdown( self, limit )
local items, priorities, indices, higherpriority, size = self, self._priorities, self._indices, self._higherpriority, self._size
for index = limit, 1, -1 do
local left = index + index
local right = left + 1
while left <= size do
local smaller = left
if right <= size and higherpriority( priorities[right], priorities[left] ) then
smaller = right
end
if higherpriority( priorities[smaller], priorities[index] ) then
items[index], items[smaller] = items[smaller], items[index]
priorities[index], priorities[smaller] = priorities[smaller], priorities[index]
indices[items[index]], indices[items[smaller]] = index, smaller
else
break
end
index = smaller
left = index + index
right = left + 1
end
end
end
local PriorityQueueMt
local PriorityQueue = {}
local function minishigher( a, b )
return a < b
end
local function maxishigher( a, b )
return a > b
end
function PriorityQueue.new( priority_or_array )
local t = type( priority_or_array )
local higherpriority = minishigher
if t == 'table' then
higherpriority = priority_or_array.higherpriority or higherpriority
elseif t == 'function' or t == 'string' then
higherpriority = priority_or_array
elseif t ~= 'nil' then
local msg = 'Wrong argument type to PriorityQueue.new, it must be table or function or string, has: %q'
error( msg:format( t ))
end
if type( higherpriority ) == 'string' then
if higherpriority == 'min' then
higherpriority = minishigher
elseif higherpriority == 'max' then
higherpriority = maxishigher
else
local msg = 'Wrong string argument to PriorityQueue.new, it must be "min" or "max", has: %q'
error( msg:format( tostring( higherpriority )))
end
end
local self = setmetatable( {
_priorities = {},
_indices = {},
_size = 0,
_higherpriority = higherpriority or minishigher
}, PriorityQueueMt )
if t == 'table' then
self:batchenq( priority_or_array )
end
return self
end
function PriorityQueue:enqueue( item, priority )
local items, priorities, indices = self, self._priorities, self._indices
if indices[item] ~= nil then
error( 'Item ' .. tostring(indices[item]) .. ' is already in the heap' )
end
local size = self._size + 1
self._size = size
items[size], priorities[size], indices[item] = item, priority, size
siftup( self, size )
return self
end
function PriorityQueue:remove( item )
local index = self._indices[item]
if index ~= nil then
local size = self._size
local items, priorities, indices = self, self._priorities, self._indices
indices[item] = nil
if size == index then
items[size], priorities[size] = nil, nil
self._size = size - 1
else
local lastitem = items[size]
items[index], priorities[index] = items[size], priorities[size]
items[size], priorities[size] = nil, nil
indices[lastitem] = index
size = size - 1
self._size = size
if size > 1 then
siftdown( self, siftup( self, index ))
end
end
return true
else
return false
end
end
function PriorityQueue:contains( item )
return self._indices[item] ~= nil
end
function PriorityQueue:update( item, priority )
local ok = self:remove( item )
if ok then
self:enqueue( item, priority )
return true
else
return false
end
end
function PriorityQueue:dequeue()
local size = self._size
assert( size > 0, 'Heap is empty' )
local items, priorities, indices = self, self._priorities, self._indices
local item, priority = items[1], priorities[1]
indices[item] = nil
if size > 1 then
local newitem = items[size]
items[1], priorities[1] = newitem, priorities[size]
items[size], priorities[size] = nil, nil
indices[newitem] = 1
size = size - 1
self._size = size
siftdown( self, 1 )
else
items[1], priorities[1] = nil, nil
self._size = 0
end
return item, priority
end
function PriorityQueue:peek()
return self[1], self._priorities[1]
end
function PriorityQueue:len()
return self._size
end
function PriorityQueue:empty()
return self._size <= 0
end
function PriorityQueue:batchenq( iparray )
local items, priorities, indices = self, self._priorities, self._indices
local size = self._size
for i = 1, #iparray, 2 do
local item, priority = iparray[i], iparray[i+1]
if indices[item] ~= nil then
error( 'Item ' .. tostring(indices[item]) .. ' is already in the heap' )
end
size = size + 1
items[size], priorities[size] = item, priority
indices[item] = size
end
self._size = size
if size > 1 then
siftdown( self, floor( size / 2 ))
end
end
PriorityQueueMt = {
__index = PriorityQueue,
__len = PriorityQueue.len,
}
return setmetatable( PriorityQueue, {
__call = function( _, ... )
return PriorityQueue.new( ... )
end
} )