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