├── 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 | [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/Yonaba/binary-heaps/trend.png)](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 --------------------------------------------------------------------------------