Adds #91 - Added Binary Heaps Lua module
parent
8d676eaec4
commit
be42334fb7
|
@ -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.
|
|
@ -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")
|
||||
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue