Adds #91 - Added Binary Heaps Lua module

master
Yonaba 2015-09-21 17:01:18 +00:00
parent 8d676eaec4
commit be42334fb7
4 changed files with 409 additions and 0 deletions

View File

@ -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.

View File

@ -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 <tt>heap.insert</tt>
* __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")

View File

@ -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 <tt>Heap:add()</tt> as alias to <tt>Heap:insert()</tt>
* Buxfix with <tt>Heap:reset()</tt>
* Deleted unused <tt>Heap:init()</tt>
* 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

256
files/lua/binary_heap.lua Normal file
View File

@ -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}
-- <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
return heap