dns-suite.js/node_modules/binaryheap/binaryheap.js

314 lines
6.6 KiB
JavaScript

// Copyright 2012 Timothy J Fontaine <tjfontaine@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE
'use strict';
var assert = require('assert');
var Heap = function(min) {
this.length = 0;
this.root = undefined;
if (min) {
this._comparator = this._smallest;
} else {
this._comparator = this._largest;
}
};
Heap.init = function(obj, key) {
obj._parent = null;
obj._left = null;
obj._right = null;
obj._key = key;
return obj;
};
Heap.prototype.count = function (node) {
if (!node) return 0;
var c = 1;
c += this.count(node._left);
c += this.count(node._right);
return c;
};
Heap.prototype.insert = function(obj, key) {
var insert, node;
this.length += 1;
node = Heap.init(obj, key);
if (!this.root) {
this.root = node;
} else {
insert = this._last();
node._parent = insert;
if (!insert._left)
insert._left = node;
else
insert._right = node;
this._up(node);
}
this._head();
return node;
};
Heap.prototype.pop = function() {
var ret, last;
if (!this.root)
return null;
return this.remove(this.root);
};
Heap.prototype.remove = function(node) {
var ret, last;
ret = node;
last = this._last();
if (last._right)
last = last._right;
else
last = last._left;
this.length -= 1;
if (!last) {
if (ret == this.root)
this.root = null;
return ret;
}
if (ret == last) {
if (ret._parent._left == node)
ret._parent._left = null;
else
ret._parent._right = null;
last = ret._parent;
ret._parent = null;
} else if (!ret._left && !ret._right) {
// we're trying to remove an element without any children and its not the last
// move the last under its parent and heap-up
if (last._parent._left == last) last._parent._left = null;
else last._parent._right = null;
if (ret._parent._left == ret) ret._parent._left = last;
else ret._parent._right = last;
last._parent = ret._parent;
ret._parent = null;
// TODO in this case we shouldn't later also do a down, but it should only visit once
this._up(last);
} else {
this._delete_swap(ret, last);
}
if (ret == this.root)
this.root = last;
this._down(last);
this._head();
return ret;
};
// TODO this probably isn't the most efficient way to ensure that we're always
// at the root of the tree, but it works for now
Heap.prototype._head = function() {
if (!this.root)
return;
var tmp = this.root;
while (tmp._parent) {
tmp = tmp._parent;
}
this.root = tmp;
};
// TODO is there a more efficient way to store this instead of an array?
Heap.prototype._last = function() {
var path, pos, mod, insert;
pos = this.length;
path = [];
while (pos > 1) {
mod = pos % 2;
pos = Math.floor(pos / 2);
path.push(mod);
}
insert = this.root;
while (path.length > 1) {
pos = path.pop();
if (pos === 0)
insert = insert._left;
else
insert = insert._right;
}
return insert;
};
Heap.prototype._swap = function(a, b) {
var cleft, cright, tparent;
cleft = b._left;
cright = b._right;
if (a._parent) {
if (a._parent._left == a) a._parent._left = b;
else a._parent._right = b;
}
b._parent = a._parent;
a._parent = b;
// This assumes direct descendents
if (a._left == b) {
b._left = a;
b._right = a._right;
if (b._right) b._right._parent = b;
} else {
b._right = a;
b._left = a._left;
if (b._left) b._left._parent = b;
}
a._left = cleft;
a._right = cright;
if (a._left) a._left._parent = a;
if (a._right) a._right._parent = a;
assert.notEqual(a._parent, a, "A shouldn't refer to itself");
assert.notEqual(b._parent, b, "B shouldn't refer to itself");
};
Heap.prototype._delete_swap = function(a, b) {
if (a._left != b) b._left = a._left;
if (a._right != b) b._right = a._right;
if (b._parent._left == b) b._parent._left = null;
else b._parent._right = null;
if (a._parent) {
if (a._parent._left == a) a._parent._left = b;
else a._parent._right = b;
}
b._parent = a._parent;
if (b._left) b._left._parent = b;
if (b._right) b._right._parent = b;
a._parent = null;
a._left = null;
a._right = null;
};
Heap.prototype._smallest = function(heap) {
var small = heap;
if (heap._left && heap._key > heap._left._key) {
small = heap._left;
}
if (heap._right && small._key > heap._right._key) {
small = heap._right;
}
return small;
};
Heap.prototype._largest = function(heap) {
var large = heap;
if (heap._left && heap._key < heap._left._key) {
large = heap._left;
}
if (heap._right && large._key < heap._right._key) {
large = heap._right;
}
return large;
};
Heap.prototype._up = function(node) {
if (!node || !node._parent)
return;
var next = this._comparator(node._parent);
if (next != node._parent) {
this._swap(node._parent, node);
this._up(node);
}
};
Heap.prototype._down = function(node) {
if (!node)
return;
var next = this._comparator(node);
if (next != node) {
this._swap(node, next);
this._down(node);
}
};
var util = require('util');
Heap.prototype.print = function(stream) {
stream.write('digraph {\n');
Heap._print(this.root, stream);
stream.write('}\n');
};
Heap._print = function(heap, stream) {
if (!heap) return;
if (heap._left) {
stream.write(util.format('' + heap._key, '->', heap._left._key, '\n'));
Heap._print(heap._left, stream);
}
if (heap._right) {
stream.write(util.format('' + heap._key, '->', heap._right._key, '\n'));
Heap._print(heap._right, stream);
}
};
module.exports = Heap;