-
Notifications
You must be signed in to change notification settings - Fork 2
/
heap
266 lines (231 loc) · 7.05 KB
/
heap
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
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
-- Copyright (c) 2012-2013 Roland Yonaba
-- An implementation of Binary Heaps data structure in pure Lua
--[[
Documentation :
- http://www.algolist.net/Data_structures/Binary_heap/Array-based_int_repr
- http://www.cs.cmu.edu/~adamchik/15-121/lectures/Binary%20Heaps/heaps.html
- http://rperrot.developpez.com/articles/algo/structures/arbres/
--]]
local require = require
local assert = assert
local ipairs = ipairs
local pairs = pairs
local floor = math.floor
local tostring = tostring
local setmetatable = setmetatable
-- Default sorting function.
-- Used for Min-Heaps creation.
local function f_min(a,b) return a < b end
-- Value lookup in a table
local indexOf = function(t,v)
for i = 1,#t do
if t[i] == v then return i end
end
return nil
end
-- Percolates up datum in the heap recursively
local function percolate_up(self,index)
local pIndex
if index > 1 then
pIndex = floor(index/2)
if self._heap[pIndex] then
if not (self.sort(self._heap[pIndex].value,self._heap[index].value)) then
self._heap[pIndex],self._heap[index] = self._heap[index],self._heap[pIndex]
percolate_up(self,pIndex) -- Recursive call from the parent index
end
else
return
end
end
end
-- Percolates down datum in the heap recursively
local function percolate_down(self,index)
local lfIndex,rtIndex,minIndex
lfIndex = 2*index
rtIndex = lfIndex + 1
if rtIndex > self.size then
if lfIndex > self.size then return
else minIndex = lfIndex end
else
if self.sort(self._heap[lfIndex].value,self._heap[rtIndex].value) then
minIndex = lfIndex
else
minIndex = rtIndex
end
end
if not self.sort(self._heap[index].value,self._heap[minIndex].value) then
self._heap[index],self._heap[minIndex] = self._heap[minIndex],self._heap[index]
percolate_down(self,minIndex) -- Recursive call from the newly shifted index
end
end
-- Minimalistic heap class constructor
local function newHeap(class,comp)
return setmetatable({_heap = {},sort = comp or f_min, size = 0},class)
end
-- The heap class
heap = setmetatable({}, {__call = function(self,...) return newHeap(self,...) end})
heap.size = 0
heap.comp = f_min
heap.__index = heap
-- Checks if a heap is empty
-- Return true or false [boolean]
function heap:empty()
return (self.size==0)
end
-- Gets heap size (the very number of elements stored in the heap)
-- Returns the heap size [number]
function heap:getSize()
return self.size
end
-- Clears the heap
-- Returns nothing [nil]
function heap:clear()
self._heap = {}
self.size = 0
return self
end
-- Returns the left child index of the current index
-- Returned index may not be a valid index in the heap
-- Returns this index [number]
function heap:leftChildIndex(index)
return (2*index)
end
-- Returns the right child index of the current index
-- Returned index may not be a valid index in the heap
-- Returns this index [number]
function heap:rightChildIndex(index)
return 2*index+1
end
-- Returns the parent index of the current index
-- Returned index may not be a valid index in the heap
-- Returns this index [number]
function heap:parentIndex(index)
return floor(index/2)
end
-- Returns the top element in the heap
-- Does not pop the heap
function heap:top()
assert(not self:empty(),'Heap is empty')
return self._heap[1].value,self._heap[1].data
end
-- Inserts a value in the heap as a table {value = value, data = data}
-- <data> Argument is optional and may represent extra information linked to <value> argument.
-- Returns nothing [nil]
function heap:insert(value,data)
self.size = self.size + 1
self._heap[self.size] = {value = value, data = data}
percolate_up(self,self.size)
return self
end
heap.add = heap.insert
-- Pops the first element in the heap
-- Returns this element unpacked: value first then data linked
function heap:pop()
assert(not self:empty(), 'Heap is empty.')
local root = self._heap[1]
self._heap[1] = self._heap[self.size]
self._heap[self.size] = nil
self.size = self.size-1
if self.size>1 then
percolate_down(self,1)
end
return root.value,root.data
end
-- Checks if the given index is valid in the heap
-- Returns the element stored in the heap at that very index [table], otherwise nil. [nil]
function heap:checkIndex(index)
return self._heap[index] or nil
end
-- Pops the first element in the heap
-- Replaces it with the given element and reorders the heap
-- The size of the heap is preserved
function heap:replace(value,data)
assert(not self:empty(), 'heap is empty, use insert()')
local root = self._heap[1]
self._heap[1] = {value = value,data = data}
percolate_down(self,1)
return root.value,root.data
end
-- Resets the heap property regards to the comparison function given as argument (Optional)
-- Returns nothing [nil]
function heap:reset(comp)
self.sort = comp or self.sort
local _heap = self._heap
self._heap = {}
self.size = 0
for i in pairs(_heap) do
self:insert(_heap[i].value,_heap[i].data)
end
return self
end
-- Appends a heap contents to the current one
-- Returns nothing [nil]
function heap:merge(other)
assert(self:isValid(),'Self heap is not a valid heap')
assert(other:isValid(),'Argument is not a valid heap')
assert(self.sort(1,2) == other.sort(1,2),'Heaps must have the same sort functions')
for i,node in ipairs(other._heap) do
self:insert(node.value,node.data)
end
return self
end
-- Shortcut for merging heaps with '+' operator
-- Returns a new heap based on h1+h2 [table]
function heap.__add(h1,h2)
local h = heap()
h:merge(h1)
h:merge(h2)
return h
end
-- Tests if each element stored in a heap is located at the right place
-- Returns true on success, false on error. [boolean]
function heap:isValid()
if self.size <= 1 then return true end
local i = 1
local lfIndex,rtIndex
for i = 1,(floor(self.size/2)) do
lfIndex = 2*i
rtIndex = lfIndex+1
if self:checkIndex(lfIndex) then
if not self.sort(self._heap[i].value,self._heap[lfIndex].value) then
return false
end
end
if self:checkIndex(rtIndex) then
if not self.sort(self._heap[i].value,self._heap[rtIndex].value) then
return false
end
end
end
return true
end
-- Restores the heap property
-- Should be used when a heap was found non-valid
function heap:heap(item)
if (self.size == 0) then return end
if item then
local i = indexOf(self.__heap,item)
if i then
percolate_down(self, i)
percolate_up(self, i)
end
return
end
for i = floor(self.size/2),1,-1 do
percolate_down(self,i)
end
return self
end
-- (Debug utility) Create a string representation of the current
-- Returns this string to be used with print() or tostring() [string]
function heap.__tostring(self)
local out = ''
for k in ipairs(self._heap) do
out = out.. (('Element %d - Value : %s\n'):format(k,tostring(self._heap[k].value)))
end
return out
end
function new()
return heap()
end
--return heap