--- A light implementation of Binary heaps data structure. -- While running a search, some search algorithms (Astar, Dijkstra, Jump Point Search) have to maintains -- a list of nodes called __open list__. Retrieve from this list the lowest cost node can be quite slow, -- as it normally requires to skim through the full set of nodes stored in this list. This becomes a real -- problem especially when dozens of nodes are being processed (on large maps). -- -- The current module implements a binary heap -- data structure, from which the search algorithm will instantiate an open list, and cache the nodes being -- examined during a search. As such, retrieving the lower-cost node is faster and globally makes the search end -- up quickly. -- -- This module is internally used by the library on purpose. -- It should normally not be used explicitely, yet it remains fully accessible. -- --[[ Notes: This lighter implementation of binary heaps, based on : https://github.com/Yonaba/Binary-Heaps --]] if (...) then -- Dependency local Utils = require((...):gsub('%.bheap$','.utils')) -- Local reference local floor = math.floor -- Default comparison function local function f_min(a,b) return a < b end -- Percolates up local function percolate_up(heap, index) if index == 1 then return end local pIndex if index <= 1 then return end if index%2 == 0 then pIndex = index/2 else pIndex = (index-1)/2 end if not heap._sort(heap._heap[pIndex], heap._heap[index]) then heap._heap[pIndex], heap._heap[index] = heap._heap[index], heap._heap[pIndex] percolate_up(heap, pIndex) end end -- Percolates down local function percolate_down(heap,index) local lfIndex,rtIndex,minIndex lfIndex = 2*index rtIndex = lfIndex + 1 if rtIndex > heap._size then if lfIndex > heap._size then return else minIndex = lfIndex end else if heap._sort(heap._heap[lfIndex],heap._heap[rtIndex]) then minIndex = lfIndex else minIndex = rtIndex end end if not heap._sort(heap._heap[index],heap._heap[minIndex]) then heap._heap[index],heap._heap[minIndex] = heap._heap[minIndex],heap._heap[index] percolate_down(heap,minIndex) end end -- Produces a new heap local function newHeap(template,comp) return setmetatable({_heap = {}, _sort = comp or f_min, _size = 0}, template) end --- The `heap` class.
-- This class is callable. -- _Therefore,_ heap(...) _is used to instantiate new heaps_. -- @type heap local heap = setmetatable({}, {__call = function(self,...) return newHeap(self,...) end}) heap.__index = heap --- Checks if a `heap` is empty -- @class function -- @treturn bool __true__ of no item is queued in the heap, __false__ otherwise -- @usage -- if myHeap:empty() then -- print('Heap is empty!') -- end function heap:empty() return (self._size==0) end --- Clears the `heap` (removes all items queued in the heap) -- @class function -- @treturn heap self (the calling `heap` itself, can be chained) -- @usage myHeap:clear() function heap:clear() self._heap = {} self._size = 0 self._sort = self._sort or f_min return self end --- Adds a new item in the `heap` -- @class function -- @tparam value item a new value to be queued in the heap -- @treturn heap self (the calling `heap` itself, can be chained) -- @usage -- myHeap:push(1) -- -- or, with chaining -- myHeap:push(1):push(2):push(4) function heap:push(item) if item then self._size = self._size + 1 self._heap[self._size] = item percolate_up(self, self._size) end return self end --- Pops from the `heap`. -- Removes and returns the lowest cost item (with respect to the comparison function being used) from the `heap`. -- @class function -- @treturn value a value previously pushed into the heap -- @usage -- while not myHeap:empty() do -- local lowestValue = myHeap:pop() -- ... -- end function heap:pop() local root if self._size > 0 then 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 end return root end --- Restores the `heap` property. -- Reorders the `heap` with respect to the comparison function being used. -- When given argument __item__ (a value existing in the `heap`), will sort from that very item in the `heap`. -- Otherwise, the whole `heap` will be cheacked. -- @class function -- @tparam[opt] value item the modified value -- @treturn heap self (the calling `heap` itself, can be chained) -- @usage myHeap:heapify() function heap:heapify(item) if self._size == 0 then return end if item then local i = Utils.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 return heap end