I wrote this heap implementation because I need it to run Dijkstra's algorithm to find paths in a game (for which reason I'd like this implementation not to be unreasonably slow...)
Sadly I don't know JavaScript well, much worse than C or C++ for sure, so I might've made some silly mistakes, I'd love You to point them out for me :)
For example, I'm unsure if keeping heap nodes in an array is an OK approach here; sadly I can't find the relevant SO post now, but I remember reading on SO that using arrays as memory pools results in a gross performance degradation in JavaScript. Keeping nodes in an array is pretty convinient for implementing a heap, but should I have refrained from this anyway?
Also I am aware that people usually tell me my coding style is horrible and unreadable, so I suppose I'm going to get similar comments here, how exactly did I fail this time?
The code seems correct though, it passes this test: https://paste.ee/p/edrYt which was generated with this C++ code: https://ideone.com/zpCtIz
Ah and I tried to stick to JSv5, I tried to refreain from using ES6 features. I hope none slipped through.
var GZKM = GZKM || {}
;(function() {
GZKM.Heap = function() {
this.arr = []
}
GZKM.Heap.prototype._parentIndex = function(i) {
return Math.floor((i+1)/2)-1
}
GZKM.Heap.prototype._leftChildIndex = function(i) {
return 2*(i+1)-1
}
GZKM.Heap.prototype._rightChildIndex = function(i) {
return 2*(i+1)
}
GZKM.Heap.prototype._swapNodes = function(node1, node2) {
// Idea taken from this SO answer: https://stackoverflow.com/a/22053474/4385532
// Doing it this way b/c this SO answer scared me that the standard way may lead to a memory leak: https://stackoverflow.com/a/872433/4385532
this.arr[node1._i] = [this.arr[node2._i], this.arr[node2._i]=this.arr[node1._i]][0]
node1._i = [node2._i, node2._i=node1._i][0]
}
GZKM.Heap.prototype._moveUp = function(node) {
var canMove = function() {
return node._i > 0
}
var mustMove = function() {
return this.arr[this._parentIndex(node._i)].priority > node.priority
}.bind(this)
while(canMove() && mustMove())
this._swapNodes(node, this.arr[this._parentIndex(node._i)])
}
GZKM.Heap.prototype._moveDown = function(node) {
var canMove = function() {
return this._leftChildIndex(node._i) < this.arr.length
}.bind(this)
var nextNode = function() {
if(this._rightChildIndex(node._i) >= this.arr.length ||
this.arr[this._rightChildIndex(node._i)].priority > this.arr[this._leftChildIndex(node._i)].priority)
return this.arr[this._leftChildIndex(node._i)]
else return this.arr[this._rightChildIndex(node._i)]
}.bind(this)
var mustMove = function() {
return nextNode().priority < node.priority
}
while(canMove() && mustMove())
this._swapNodes(node, nextNode())
}
GZKM.Heap.prototype.push = function(priority, elt) {
var eltIndex = this.arr.length;
this.arr.push({elt: elt, priority: priority, _i: eltIndex})
var ret = this.arr[eltIndex]
this._moveUp(this.arr[eltIndex])
return ret
}
GZKM.Heap.prototype.pop = function() {
if(!this.arr.length) return undefined
this._swapNodes(this.arr[0], this.arr[this.arr.length-1])
var ret = this.arr.pop()
this._moveDown(this.arr[0])
return ret
}
GZKM.Heap.prototype.reprioritize = function(node, newPriority) {
node.priority = newPriority
this._moveUp(node)
this._moveDown(node)
}
})()