binaryheapx
Version:
JavaScript Implementation of the Binary Heap.
280 lines (251 loc) • 6.79 kB
JavaScript
/* ================================================================
* BinaryHeap by xdf(xudafeng[at]126.com)
*
* first created at : Wed Aug 20 2014 10:35:14 GMT+0800 (CST)
*
* ================================================================
* Copyright 2014 xdf
*
* Licensed under the MIT License
* You may not use this file except in compliance with the License.
*
* ================================================================ */
;(function(root, factory){
'use strict';
/* amd like aml https://github.com/xudafeng/aml.git */
if (typeof define === 'function' && define.amd) {
return define(['exports'], factory);
} else if (typeof exports !== 'undefined') {
return factory(exports);
} else{
/* browser */
factory(root['BinaryHeap'] || (root['BinaryHeap'] = {}));
}
})(this, function(exports, undefined) {
/**
* @constructor BinaryHeap
* @param {Number} capacity
* @param {Boolean} isMinHeap
* @param {Array} comparator
*/
function Constructor() {
var args = arguments;
var capacity = Infinity;
var comparator = function(a, b) {
return a - b;
};
var isMinHeap = true;
if (typeof args[2] !== 'undefined') {
capacity = args[0];
isMinHeap = args[1];
comparator = args[2];
} else {
if (typeof args[1] !== 'undefined') {
if (typeof args[0] === 'number' && typeof args[1] === 'boolean') {
capacity = args[0];
isMinHeap = args[1];
} else if (typeof args[0] === 'boolean' && typeof args[1] === 'function') {
isMinHeap = args[0];
comparator = args[1];
} else if (typeof args[0] === 'number' && typeof args[1] === 'function') {
capacity = args[0];
comparator = args[1];
}
} else {
if (typeof args[0] === 'number') {
capacity = args[0];
} else if (typeof args[0] === 'function'){
comparator = args[0];
} else if (typeof args[0] === 'boolean'){
isMinHeap = args[0];
}
}
}
// create heap
this.list = [];
this.isMinHeap = isMinHeap;
this.capacity = capacity;
this.comparator = comparator;
}
var proto = Constructor.prototype;
/**
* Adds an object to this heap. Same as insert(Object).
* @method add
*/
proto.add = function(object) {
return this.insert(object);
}
/**
* Clears all elements from queue.
* @method clear
*/
proto.clear = function() {
this.list = [];
return this;
}
/**
* Returns the priority element. Same as peek().
* @method get
*/
proto.get = function() {
return this.peek();
}
/**
* Inserts an element into queue.
* @method insert
* @param {Object} object
* @return {Boolean}
*/
proto.insert = function(object) {
if (this.isFull()) {
throw('Error: insert is not success due to the lack of capacity.');
} else {
this.list.push(object);
this.percolateUp(this.size() - 1);
}
return true;
}
/**
* Tests if queue is empty.
* @method isEmpty
* @return {Boolean}
*/
proto.isEmpty = function() {
return this.size() === 0;
}
/**
* Tests if queue is full.
* @method isFull
* @return {Boolean}
*/
proto.isFull = function() {
return this.size() === this.capacity;
}
/**
* Returns an iterator over this heap's elements.
* @method iterator
* @return {Object}
* TODO
*/
proto.iterator = function() {
return this;
}
/**
* Returns the element on top of heap but don't remove it.
* @method peek
* @return {Object}
*/
proto.peek = function() {
return this.isEmpty()? null: this.list[0];
}
/**
* Returns the element on top of heap and remove it.
* @method pop
* @return {Object}
*/
proto.pop = function() {
var heapTop = this.list[0];
var heapEnd = this.list.pop();
// sink down it
if (!this.isEmpty()) {
this.list[0] = heapEnd;
this.percolateDown(0);
}
return heapTop;
}
/**
* Removes the priority element. Same as pop().
* @method remove
*/
proto.remove = function() {
return this.pop();
}
/**
* Returns the number of elements in this heap.
* @method size
* @return {Number}
*/
proto.size = function() {
return this.list.length;
}
/**
* Returns a string representation of this heap.
* @method toString
* @return {String}
*/
proto.toString = function() {
return JSON.stringify(this.list);
}
/**
* Tests if queue contains item.
* @method contains
* @return {Boolean}
*/
proto.contains = function(object) {
return !!~this.list.indexOf(object);
}
/**
* Increases the size of the heap to support additional elements
* @method grow
*/
proto.grow = function() {
this.capacity ++;
return this;
}
/**
* Percolates element up heap from from the position given by the index.
* @method percolateUp
*/
proto.percolateUp = function(index) {
// last element
var currentNode = this.list[index];
while (index > 0) {
/**
* parent node index is Math.floor((index + 1) / 2) - 1
* because the first index is 1
*/
var parentIndex = Math.floor((index + 1) / 2) - 1,
parentNode = this.list[parentIndex];
var temp = this.comparator(currentNode, parentNode);
temp = this.isMinHeap? temp > 0: temp < 0;
if (temp) break;
// swap the parent node with the currentNode
this.list[parentIndex] = currentNode;
this.list[index] = parentNode;
index = parentIndex;
}
return this;
}
/**
* Percolates element down heap from the position given by the index.
* @method percolateDown
*/
proto.percolateDown = function(index) {
var currentNode = this.list[index];
while (true) {
var childRightIndex = (index + 1) * 2;
var childLeftIndex = childRightIndex - 1;
var swap = null;
if (childLeftIndex < this.size()) {
var childLeft = this.list[childLeftIndex];
var temp = this.comparator(childLeft, currentNode);
temp = this.isMinHeap? temp < 0: temp > 0;
if (temp) swap = childLeftIndex;
}
if (childRightIndex < this.size()) {
var childRight = this.list[childRightIndex];
var temp = this.comparator(childRight, swap === null ? currentNode : childLeft);
temp = this.isMinHeap? temp < 0: temp > 0;
if (temp) swap = childRightIndex;
}
if (swap == null) break;
this.list[index] = this.list[swap];
this.list[swap] = currentNode;
index = swap;
}
return this;
}
exports.Constructor = Constructor;
exports.version = '0.1.0';
});
/* vim: set sw=4 ts=4 et tw=80 : */