/**
* @memberOf Util
* @class Heap
* @classdesc Heap ヒープ(二分ヒープ)
* <p>
* 最小値(最大値)を効率よく取り出すことができるデータ構造
* <p>
* 参考 {@link http://d.hatena.ne.jp/otaks/20121220/1355993039}
*
* @param {Function}
* [func=function(obj){ return +obj; }]
* pushで登録するオブジェクトからヒープの大小比較判定値を取り出す関数
* @param {Function}
* [delFunc=function(obj){ return obj; }] 削除対象ノードを特定する関数<br>
* 「delの引数オブジェクト === delFunc(heapのノードのオブジェクト)」 で判定する
*
* @example h = HJN_util.Heap( function(obj){ return +obj; } );<br>
* h.push("12.34") // データを登録する ;<br>
* h.push(0.12) // ;<br>
* h.pop() // => 0.12 最小値のオブジェクトを取り出す ;<br>
* h.pop() // => "12.34" ;<br>
* h.top() // =>undefined 最小値のオブジェクト ;<br>
* h.size() // =>0 登録オブジェクト数
*/
export default (function() { // #55
/** @constructor */
function Heap(func, delFunc){
if(!(this instanceof Heap)) return new Heap(func, delFunc);
this._func = (typeof(func) === "function")
? func
: function(node){
if (typeof(node.val) === "undefined") { return node }
else { return node.val};
};
this._delFunc = (typeof(delFunc) === "function")
? delFunc
: function(node){
if(typeof(node.obj) === "undefined"){ return node }
else { return node.obj};
};
this._heap = []; // Heap構造体(大小比較数値用)
}
//
/**
* upheap:root方向へheapを再構築する
*
* @private
* @param {Number}
* k 起点の_heap配列位置
*/
Heap.prototype._upHeap = function(k) {
// 指定位置のオブジェクトとその値の控えを取る
var obj = this._heap[k];
var val = this._func(obj);
while( 0 < k ) {
// 親ノードの配列位置を求める
var p = Math.floor( (k - 1) / 2 );
// 逆転していなければ処理終了
if( this._func(this._heap[p]) <= val ) break;
// 親ノードと処理対象を入れ替える
this._heap[k] = this._heap[p];
this._heap[p] = obj;
k = p;
}
};
/**
* downheap:reaf方向へheapを再構築する
*
* @private
* @param {Number}
* k 起点の_heap配列位置
*/
Heap.prototype._downHeap = function(k) {
// 指定位置のオブジェクトとその値を控えを取る
var obj = this._heap[k];
var val = this._func(obj);
var size = this._heap.length;
// 子ノードが無くなるまで繰り返す
while( k * 2 + 1 < size) {
var a = k * 2 + 1; // 子ノード(左)
var b = a + 1; // 子ノード(右)
// aが大小比較対象先となる小さい子ノードを指すようにする
if( b < size && this._func(this._heap[a]) > this._func(this._heap[b]) ) a = b;
// 逆転していなければ処理終了
if( val < this._func(this._heap[a]) ) break;
// 子ノード(比較対象)と処理対象を入れ替える
this._heap[k] = this._heap[a];
this._heap[a] = obj;
k = a;
}
};
/**
* _heapのk番目を削除し、_heapを再構築する
*
* @private
* @param {Number}
* k 起点の_heap配列位置
* @return {object|undefined} 削除したオブジェクト(削除対象が無いとき undefined)
*/
Heap.prototype._del = function(k) {
if (this._heap.length <= k) return undefined; // ヒープ外を指定したとき
var ret = this._heap[k]; // 削除したオブジェクト
var obj = this._heap.pop(); // 末尾ノードを取り出す
var size = this._heap.length;
if (k === size) return ret; // 末尾ノードを削除したとき再構築不要
this._heap[k] = obj; // 末尾ノードを指定位置に移動する
if (size === 1) return ret; // ヒープが1個のとき、再構築不要
// upHeapの判定
var val = this._func(obj); // 末尾ノードにあったオブジェクトの値
var p = Math.floor( (k - 1) / 2 );
if (0 < k && val < this._func(this._heap[p])) {
// 親ノードより小さいときupheapする
this._upHeap(k);
} else {
var a = k * 2 + 1; // 子ノード(左)
var b = a + 1; // 子ノード(右)
if (a < size) { // 子ノードがあるとき(末端の枝葉でないとき)のみdownHeapする
// aに、子ノードの小さい方のノードを設定する
if( b < size && (this._func(this._heap[a]) > this._func(this._heap[b])) ) a = b;
// 自分より小さい子ノードがあるとき、downheapする
if( this._func(this._heap[a]) < val ) this._downHeap(k);
}
}
return ret;
};
// public
/**
* データを追加する
*
* @memberOf Util.Heap
* @param {Object}
* obj 登録オブジェクト
*/
Heap.prototype.push = function(obj) {
// 末尾に追加し、upHeapする
this._heap.push(obj);
this._upHeap(this._heap.length - 1);
if (this._deletable) {
this._deleteMap = {};
}
};
/**
* 最小値のデータを取り出す
*
* @memberOf Util.Heap
* @return {Object|undefined} 最小値
*/
Heap.prototype.pop = function() {
// 先頭ノードを戻り値用に退避する
var ret = this._heap[0];
// 末尾ノードを退避し削除する
var obj = this._heap.pop();
if(0 < this._heap.length){
// ヒープが空でないとき、末尾ノードを先頭に移動し、downHeapする
this._heap[0] = obj;
this._downHeap(0);
}
return ret;
};
/**
* 指定データを削除する
*
* @memberOf Util.Heap
* @param {Object}
* obj 削除対象と同一オブジェクト(=== で判定)
* @return {Object|undefined} 削除したオブジェクト(undefined:合致するオブジェクトが無いとき)
*/
Heap.prototype.del = function(obj) { // #59
// 削除対象オブジェクトのHeap配列位置を取得する
var k = -1;
if (this._heap.some(find, this)){
// 合致するオブジェクトのノードを削除し、合致ノードをリターンする
return this._del(k);
}
// 合致するオブジェクトが無いとき
return undefined;
function find(e, i) {
if(this._delFunc(e) === obj){
k = i;
return true;
}
return false;
}
};
/**
* 最小値を返却する(登録データは変更しない)
*
* @memberOf Util.Heap
* @return {Object|undefined} 最小値
*/
Heap.prototype.top = function() {
return this._heap[0];
};
/**
* ヒープのサイズを返却する
*
* @memberOf Util.Heap
* @return {Number} ヒープサイズ(0以上)
*/
Heap.prototype.size = function() {
return this._heap.length;
};
/* new */
return Heap;
}());