├── MIT-LICENSE.txt
├── README.md
├── binary_heap.lua
└── version_history.md
/MIT-LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2012-2013 Roland Yonaba
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a
4 | copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be included
12 | in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | #Binary-Heaps#
2 | Implementation of *binary heaps* data structure in pure Lua
3 |
4 |
5 | ##Usage##
6 | Add 'binary_heap.lua' file inside your project.
7 | Call it using __require__ function.
8 | It will return a table containing a set of functions, acting as a class.
9 |
10 | ##API##
11 |
12 |
13 | * __heap:new()__ : Returns a new heap ( a Min-Heap by default).
14 | * __heap()__ : Same as heap:new()
15 | * __heap:empty()__ : Checks if a heap is empty.
16 | * __heap:getSize()__ : Returns the size of the heap.
17 | * __heap:clear()__ : Clears a heap
18 | * __heap:leftChildIndex(index)__ : Returns the left child index of element at position index in the heap
19 | * __heap:rightChildIndex(index)__ : Returns the right child index of element at position index in the heap
20 | * __heap:parentIndex(index)__ : Returns the parent index of element at position index in the heap
21 | * __heap:insert(value,linkedData)__ : Inserts value with linked data in the heap and percolates it up at its proper place.
22 | * __heap:add(value, linkedData)__ : Alias to heap.insert
23 | * __heap:replace(value,linkedData)__ : Saves the top of the heap, adds a new element at the top and reorders the heap.
24 | * __heap:pop()__ : Pops the top element, reorders the heap and returns this element unpacked : value first then data linked
25 | * __heap:checkIndex()__ : checks existence of an element at position index in the heap.
26 | * __heap:reset(function)__ : Reorders the current heap regards to the new comparison function given as argument
27 | * __heap:merge(other)__ : merge the current heap with another
28 | * __heap:isValid()__ : Checks if a heap is valid
29 | * __heap:heap(item)__ : Restores the heap property (in case the heap was earlier found non-valid)
30 |
31 | ##Additionnal features##
32 |
33 | ```lua
34 | h1+h2 : Returns a new heap with all data stored inside h1 and h2 heaps
35 | tostring(h) : Returns a string representation of heap h
36 | print(h) : Prints current heap h as a string
37 | ```
38 | By default, you create Min-heaps. If you do need __Max-heaps__, you can easily create them this way:
39 |
40 | ```lua
41 | local comp = function(a,b) return a>b end
42 | local myHeap = heap(comp)
43 | ```
44 |
45 | ##Chaining##
46 | Some functions can be chained together, as they return the heap itself:
47 |
48 | ```lua
49 | heap:clear()
50 | heap:add() or heap:insert()
51 | heap:reset()
52 | heap:merge()
53 | heap:heap()
54 | ```
55 |
56 | Example:
57 |
58 | ```lua
59 | h = Heap()
60 | h:add(1):add(2):heap():clear():add(3):add(4):merge(Heap()):reset()
61 | print(h)
62 | ```
63 |
64 | #Documentation used#
65 | * [Algolist.net data structure course][]
66 | * [Victor S.Adamchik's Lecture on Cs.cmu.edu][]
67 | * [RPerrot's Article on Developpez.com][]
68 |
69 | ##License##
70 | This work is under MIT-LICENSE
71 | Copyright (c) 2012 Roland Yonaba
72 |
73 | Permission is hereby granted, free of charge, to any person obtaining a
74 | copy of this software and associated documentation files (the
75 | "Software"), to deal in the Software without restriction, including
76 | without limitation the rights to use, copy, modify, merge, publish,
77 | distribute, sublicense, and/or sell copies of the Software, and to
78 | permit persons to whom the Software is furnished to do so, subject to
79 | the following conditions:
80 |
81 | The above copyright notice and this permission notice shall be included
82 | in all copies or substantial portions of the Software.
83 |
84 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
85 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
86 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
87 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
88 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
89 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
90 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
91 |
92 | [Algolist.net data structure course]: http://www.algolist.net/Data_structures/Binary_heap/Array-based_int_repr
93 | [Victor S.Adamchik's Lecture on Cs.cmu.edu]: http://www.cs.cmu.edu/~adamchik/15-121/lectures/Binary%20Heaps/heaps.html
94 | [RPerrot's Article on Developpez.com]: http://rperrot.developpez.com/articles/algo/structures/arbres/
95 | [Lua Class System]: http://yonaba.github.com/Lua-Class-System/
96 |
97 | [](https://bitdeli.com/free "Bitdeli Badge")
98 |
99 |
--------------------------------------------------------------------------------
/binary_heap.lua:
--------------------------------------------------------------------------------
1 | -- Copyright (c) 2012-2013 Roland Yonaba
2 | -- An implementation of Binary Heaps data structure in pure Lua
3 |
4 | --[[
5 | Documentation :
6 | - http://www.algolist.net/Data_structures/Binary_heap/Array-based_int_repr
7 | - http://www.cs.cmu.edu/~adamchik/15-121/lectures/Binary%20Heaps/heaps.html
8 | - http://rperrot.developpez.com/articles/algo/structures/arbres/
9 | --]]
10 |
11 |
12 |
13 | local require = require
14 | local assert = assert
15 | local ipairs = ipairs
16 | local pairs = pairs
17 | local floor = math.floor
18 | local tostring = tostring
19 | local setmetatable = setmetatable
20 |
21 | -- Default sorting function.
22 | -- Used for Min-Heaps creation.
23 | local function f_min(a,b) return a < b end
24 |
25 | -- Value lookup in a table
26 | local indexOf = function(t,v)
27 | for i = 1,#t do
28 | if t[i] == v then return i end
29 | end
30 | return nil
31 | end
32 |
33 | -- Percolates up datum in the heap recursively
34 | local function percolate_up(self,index)
35 | local pIndex
36 | if index > 1 then
37 | pIndex = floor(index/2)
38 | if self._heap[pIndex] then
39 | if not (self.sort(self._heap[pIndex].value,self._heap[index].value)) then
40 | self._heap[pIndex],self._heap[index] = self._heap[index],self._heap[pIndex]
41 | percolate_up(self,pIndex) -- Recursive call from the parent index
42 | end
43 | else
44 | return
45 | end
46 | end
47 | end
48 |
49 | -- Percolates down datum in the heap recursively
50 | local function percolate_down(self,index)
51 | local lfIndex,rtIndex,minIndex
52 | lfIndex = 2*index
53 | rtIndex = lfIndex + 1
54 | if rtIndex > self.size then
55 | if lfIndex > self.size then return
56 | else minIndex = lfIndex end
57 | else
58 | if self.sort(self._heap[lfIndex].value,self._heap[rtIndex].value) then
59 | minIndex = lfIndex
60 | else
61 | minIndex = rtIndex
62 | end
63 | end
64 | if not self.sort(self._heap[index].value,self._heap[minIndex].value) then
65 | self._heap[index],self._heap[minIndex] = self._heap[minIndex],self._heap[index]
66 | percolate_down(self,minIndex) -- Recursive call from the newly shifted index
67 | end
68 | end
69 |
70 | -- Minimalistic heap class constructor
71 | local function newHeap(class,comp)
72 | return setmetatable({_heap = {},sort = comp or f_min, size = 0},class)
73 | end
74 |
75 | -- The heap class
76 | local heap = setmetatable({}, {__call = function(self,...) return newHeap(self,...) end})
77 | heap.__index = heap
78 |
79 | -- Checks if a heap is empty
80 | -- Return true or false [boolean]
81 | function heap:empty()
82 | return (self.size==0)
83 | end
84 |
85 | -- Gets heap size (the very number of elements stored in the heap)
86 | -- Returns the heap size [number]
87 | function heap:getSize()
88 | return self.size
89 | end
90 |
91 | -- Clears the heap
92 | -- Returns nothing [nil]
93 | function heap:clear()
94 | self._heap = {}
95 | self.size = 0
96 | return self
97 | end
98 |
99 | -- Returns the left child index of the current index
100 | -- Returned index may not be a valid index in the heap
101 | -- Returns this index [number]
102 | function heap:leftChildIndex(index)
103 | return (2*index)
104 | end
105 |
106 | -- Returns the right child index of the current index
107 | -- Returned index may not be a valid index in the heap
108 | -- Returns this index [number]
109 | function heap:rightChildIndex(index)
110 | return 2*index+1
111 | end
112 |
113 | -- Returns the parent index of the current index
114 | -- Returned index may not be a valid index in the heap
115 | -- Returns this index [number]
116 | function heap:parentIndex(index)
117 | return floor(index/2)
118 | end
119 |
120 | -- Returns the top element in the heap
121 | -- Does not pop the heap
122 | function heap:top()
123 | assert(not self:empty(),'Heap is empty')
124 | return self._heap[1].value,self._heap[1].data
125 | end
126 |
127 | -- Inserts a value in the heap as a table {value = value, data = data}
128 | -- Argument is optional and may represent extra information linked to argument.
129 | -- Returns nothing [nil]
130 | function heap:insert(value,data)
131 | self.size = self.size + 1
132 | self._heap[self.size] = {value = value, data = data}
133 | percolate_up(self,self.size)
134 | return self
135 | end
136 | heap.add = heap.insert
137 |
138 | -- Pops the first element in the heap
139 | -- Returns this element unpacked: value first then data linked
140 | function heap:pop()
141 | assert(not self:empty(), 'Heap is empty.')
142 | local root = self._heap[1]
143 | self._heap[1] = self._heap[self.size]
144 | self._heap[self.size] = nil
145 | self.size = self.size-1
146 | if self.size>1 then
147 | percolate_down(self,1)
148 | end
149 | return root.value,root.data
150 | end
151 |
152 | -- Checks if the given index is valid in the heap
153 | -- Returns the element stored in the heap at that very index [table], otherwise nil. [nil]
154 | function heap:checkIndex(index)
155 | return self._heap[index] or nil
156 | end
157 |
158 | -- Pops the first element in the heap
159 | -- Replaces it with the given element and reorders the heap
160 | -- The size of the heap is preserved
161 | function heap:replace(value,data)
162 | assert(not self:empty(), 'heap is empty, use insert()')
163 | local root = self._heap[1]
164 | self._heap[1] = {value = value,data = data}
165 | percolate_down(self,1)
166 | return root.value,root.data
167 | end
168 |
169 | -- Resets the heap property regards to the comparison function given as argument (Optional)
170 | -- Returns nothing [nil]
171 | function heap:reset(comp)
172 | self.sort = comp or self.sort
173 | local _heap = self._heap
174 | self._heap = {}
175 | self.size = 0
176 | for i in pairs(_heap) do
177 | self:insert(_heap[i].value,_heap[i].data)
178 | end
179 | return self
180 | end
181 |
182 | -- Appends a heap contents to the current one
183 | -- Returns nothing [nil]
184 | function heap:merge(other)
185 | assert(self:isValid(),'Self heap is not a valid heap')
186 | assert(other:isValid(),'Argument is not a valid heap')
187 | assert(self.sort(1,2) == other.sort(1,2),'Heaps must have the same sort functions')
188 | for i,node in ipairs(other._heap) do
189 | self:insert(node.value,node.data)
190 | end
191 | return self
192 | end
193 |
194 | -- Shortcut for merging heaps with '+' operator
195 | -- Returns a new heap based on h1+h2 [table]
196 | function heap.__add(h1,h2)
197 | local h = heap()
198 | h:merge(h1)
199 | h:merge(h2)
200 | return h
201 | end
202 |
203 | -- Tests if each element stored in a heap is located at the right place
204 | -- Returns true on success, false on error. [boolean]
205 | function heap:isValid()
206 | if self.size <= 1 then return true end
207 | local i = 1
208 | local lfIndex,rtIndex
209 | for i = 1,(floor(self.size/2)) do
210 | lfIndex = 2*i
211 | rtIndex = lfIndex+1
212 | if self:checkIndex(lfIndex) then
213 | if not self.sort(self._heap[i].value,self._heap[lfIndex].value) then
214 | return false
215 | end
216 | end
217 | if self:checkIndex(rtIndex) then
218 | if not self.sort(self._heap[i].value,self._heap[rtIndex].value) then
219 | return false
220 | end
221 | end
222 | end
223 | return true
224 | end
225 |
226 |
227 | -- Restores the heap property
228 | -- Should be used when a heap was found non-valid
229 | function heap:heap(item)
230 | if (self.size == 0) then return end
231 | if item then
232 | local i = indexOf(self.__heap,item)
233 | if i then
234 | percolate_down(self, i)
235 | percolate_up(self, i)
236 | end
237 | return
238 | end
239 | for i = floor(self.size/2),1,-1 do
240 | percolate_down(self,i)
241 | end
242 | return self
243 | end
244 |
245 |
246 | -- (Debug utility) Create a string representation of the current
247 | -- Returns this string to be used with print() or tostring() [string]
248 | function heap.__tostring(self)
249 | local out = ''
250 | for k in ipairs(self._heap) do
251 | out = out.. (('Element %d - Value : %s\n'):format(k,tostring(self._heap[k].value)))
252 | end
253 | return out
254 | end
255 |
256 | return heap
--------------------------------------------------------------------------------
/version_history.md:
--------------------------------------------------------------------------------
1 | #Version history#
2 |
3 | ##1.5.1 (03/27/2013)
4 | * `heap()` handles an optional arg `item`
5 | * `heap()` now returns in case it wa found empty.
6 |
7 | ##1.5 (08/27/12)
8 | * Added chaining
9 | * Added Heap:add() as alias to Heap:insert()
10 | * Buxfix with Heap:reset()
11 | * Deleted unused Heap:init()
12 | * Code cleaning, Indentation Fixed
13 |
14 | ##1.4 (08/01/2012)
15 | * Made the current module independant from [LuaClassSystem][]
16 |
17 | ##1.3 (06/13/2012)
18 | * Name clashing fixed : size() was renamed getSize()
19 |
20 | ##1.2 (05/28/12)
21 | * Updated third-party library (Lua Class System)
22 | * Added version_history.md
23 |
24 | ##1.1 (05/25/12)
25 | * Converted to module
26 |
27 | ##1.0 (05/21/12)
28 | * Heap class and instances now managed with Lua Class System
29 | * Internal class structure modified, items now stored in a private "_heap" field
30 | * Added heap:init(), heap:top(), heap:replace(), heap:heap()
31 |
32 | ##0.3 (05/14/12)
33 | * Initial Release
34 |
35 | [LuaClassSystem]: https://github.com/Yonaba/Lua-Class-System
--------------------------------------------------------------------------------