From be42334fb70b613ebcb5d21574c8bbfcc6e47308 Mon Sep 17 00:00:00 2001 From: Yonaba Date: Mon, 21 Sep 2015 17:01:18 +0000 Subject: [PATCH] Adds #91 - Added Binary Heaps Lua module --- files/docs/binary-heaps/MIT-LICENSE.txt | 20 ++ files/docs/binary-heaps/README.md | 98 ++++++++ files/docs/binary-heaps/version_history.md | 35 +++ files/lua/binary_heap.lua | 256 +++++++++++++++++++++ 4 files changed, 409 insertions(+) create mode 100644 files/docs/binary-heaps/MIT-LICENSE.txt create mode 100644 files/docs/binary-heaps/README.md create mode 100644 files/docs/binary-heaps/version_history.md create mode 100644 files/lua/binary_heap.lua diff --git a/files/docs/binary-heaps/MIT-LICENSE.txt b/files/docs/binary-heaps/MIT-LICENSE.txt new file mode 100644 index 0000000..e758895 --- /dev/null +++ b/files/docs/binary-heaps/MIT-LICENSE.txt @@ -0,0 +1,20 @@ +Copyright (c) 2012-2013 Roland Yonaba + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/files/docs/binary-heaps/README.md b/files/docs/binary-heaps/README.md new file mode 100644 index 0000000..2f7ab6b --- /dev/null +++ b/files/docs/binary-heaps/README.md @@ -0,0 +1,98 @@ +#Binary-Heaps# +Implementation of *binary heaps* data structure in pure Lua + + +##Usage## +Add 'binary_heap.lua' file inside your project. +Call it using __require__ function. +It will return a table containing a set of functions, acting as a class. + +##API## + + +* __heap:new()__ : Returns a new heap ( a Min-Heap by default). +* __heap()__ : Same as heap:new() +* __heap:empty()__ : Checks if a heap is empty. +* __heap:getSize()__ : Returns the size of the heap. +* __heap:clear()__ : Clears a heap +* __heap:leftChildIndex(index)__ : Returns the left child index of element at position index in the heap +* __heap:rightChildIndex(index)__ : Returns the right child index of element at position index in the heap +* __heap:parentIndex(index)__ : Returns the parent index of element at position index in the heap +* __heap:insert(value,linkedData)__ : Inserts value with linked data in the heap and percolates it up at its proper place. +* __heap:add(value, linkedData)__ : Alias to heap.insert +* __heap:replace(value,linkedData)__ : Saves the top of the heap, adds a new element at the top and reorders the heap. +* __heap:pop()__ : Pops the top element, reorders the heap and returns this element unpacked : value first then data linked +* __heap:checkIndex()__ : checks existence of an element at position index in the heap. +* __heap:reset(function)__ : Reorders the current heap regards to the new comparison function given as argument +* __heap:merge(other)__ : merge the current heap with another +* __heap:isValid()__ : Checks if a heap is valid +* __heap:heap(item)__ : Restores the heap property (in case the heap was earlier found non-valid) + +##Additionnal features## + +```lua +h1+h2 : Returns a new heap with all data stored inside h1 and h2 heaps +tostring(h) : Returns a string representation of heap h +print(h) : Prints current heap h as a string +``` +By default, you create Min-heaps. If you do need __Max-heaps__, you can easily create them this way: + +```lua +local comp = function(a,b) return a>b end +local myHeap = heap(comp) +``` + +##Chaining## +Some functions can be chained together, as they return the heap itself: + +```lua +heap:clear() +heap:add() or heap:insert() +heap:reset() +heap:merge() +heap:heap() +``` + +Example: + +```lua +h = Heap() +h:add(1):add(2):heap():clear():add(3):add(4):merge(Heap()):reset() +print(h) +``` + +#Documentation used# +* [Algolist.net data structure course][] +* [Victor S.Adamchik's Lecture on Cs.cmu.edu][] +* [RPerrot's Article on Developpez.com][] + +##License## +This work is under MIT-LICENSE +Copyright (c) 2012 Roland Yonaba + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +[Algolist.net data structure course]: http://www.algolist.net/Data_structures/Binary_heap/Array-based_int_repr +[Victor S.Adamchik's Lecture on Cs.cmu.edu]: http://www.cs.cmu.edu/~adamchik/15-121/lectures/Binary%20Heaps/heaps.html +[RPerrot's Article on Developpez.com]: http://rperrot.developpez.com/articles/algo/structures/arbres/ +[Lua Class System]: http://yonaba.github.com/Lua-Class-System/ + +[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/Yonaba/binary-heaps/trend.png)](https://bitdeli.com/free "Bitdeli Badge") + diff --git a/files/docs/binary-heaps/version_history.md b/files/docs/binary-heaps/version_history.md new file mode 100644 index 0000000..6dfbe86 --- /dev/null +++ b/files/docs/binary-heaps/version_history.md @@ -0,0 +1,35 @@ +#Version history# + +##1.5.1 (03/27/2013) +* `heap()` handles an optional arg `item` +* `heap()` now returns in case it wa found empty. + +##1.5 (08/27/12) +* Added chaining +* Added Heap:add() as alias to Heap:insert() +* Buxfix with Heap:reset() +* Deleted unused Heap:init() +* Code cleaning, Indentation Fixed + +##1.4 (08/01/2012) +* Made the current module independant from [LuaClassSystem][] + +##1.3 (06/13/2012) +* Name clashing fixed : size() was renamed getSize() + +##1.2 (05/28/12) +* Updated third-party library (Lua Class System) +* Added version_history.md + +##1.1 (05/25/12) +* Converted to module + +##1.0 (05/21/12) +* Heap class and instances now managed with Lua Class System +* Internal class structure modified, items now stored in a private "_heap" field +* Added heap:init(), heap:top(), heap:replace(), heap:heap() + +##0.3 (05/14/12) +* Initial Release + +[LuaClassSystem]: https://github.com/Yonaba/Lua-Class-System \ No newline at end of file diff --git a/files/lua/binary_heap.lua b/files/lua/binary_heap.lua new file mode 100644 index 0000000..ec72b83 --- /dev/null +++ b/files/lua/binary_heap.lua @@ -0,0 +1,256 @@ +-- 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 +local heap = setmetatable({}, {__call = function(self,...) return newHeap(self,...) end}) +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} +-- Argument is optional and may represent extra information linked to 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 + +return heap \ No newline at end of file