Skip to content

Commit

Permalink
faster to split bubbledown into slideup+bubbleup; 1.0.5
Browse files Browse the repository at this point in the history
  • Loading branch information
Andras committed Jan 7, 2015
1 parent cda0e4f commit c0d61ba
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 57 deletions.
89 changes: 35 additions & 54 deletions lib/qheap.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,26 +27,22 @@ Heap.prototype._swap = function Heap_swap( i, j ) {
}

Heap.prototype.insert = function Heap_insert( item ) {
var len = (this.length += 1);
// insert new item at end, and bubble up
var list = this._list;

var comparfn = this._comparFunc;
// runs 5% faster with this redundant insert
list[len] = item;
var idx = (this.length += 1);
// runs 5% faster with this redundant insert (prefetch?)
list[idx] = item;

var idx = len;
while (idx > 1) {
var vp = list[idx >> 1];
if (comparfn(item, vp) < 0) {
list[idx] = vp;
var parentval = list[idx >> 1];
if (comparfn(item, parentval) < 0) {
list[idx] = parentval;
idx = idx >> 1;
}
else {
list[idx] = item;
return;
}
else break;
}
list[1] = item;
list[idx] = item;
};
Heap.prototype.append = Heap.prototype.insert;
Heap.prototype.push = Heap.prototype.insert;
Expand All @@ -57,53 +53,38 @@ Heap.prototype.peek = function Heap_peek( ) {

Heap.prototype.remove = function Heap_remove( ) {
var len = this.length;
if (len < 1) return undefined;
var list = this._list;
var ret = list[1];

if (len < 2) {
if (len < 1) return undefined;
list[1] = undefined;
this.length = 0;
return ret;
var item = list[len];
list[len] = undefined;
this.length = (len -= 1);

// removing the root item left a hole. We fill fill it by
// sliding up the least-values path all the way from the leaf node,
// then moving the very last item into that leaf position and bubbling
// it up from there. Splitting into slide + bubbleup is 20% faster
// for a sequence of repeated insert/removes than the classic combined
// bubble-down with 3-way min() test (caching effect? The least-values
// path is more likely to be reused, ie remain cache resident.)
var comparfn = this._comparFunc;
var idx, child;
for (idx = 1, child = 2; child <= len; child = child << 1) {
if (child < len && comparfn(list[child+1], list[child]) < 0) child += 1;
list[idx] = list[child];
idx = child;
}
else {
var list = this._list;
var idx = 1;
var item = list[len];

list[len] = undefined;
this.length = (len -= 1);

// removing the root item left a hole. We will put the last item
// in the array into that hole, but first we bubble down the hole to a
// position where the left/right children will be in correct order.
var comparfn = this._comparFunc;
while (true) {
var l = 2 * idx, r = 2 * idx + 1;
if (r <= len && comparfn(list[r], item) < 0) {
if (comparfn(list[l], list[r]) < 0) {
// left child comes before item or right child, move hole down
list[idx] = list[l];
idx = l;
}
else {
// right child comes before item, move hole down
list[idx] = list[r];
idx = r;
}
}
else if (l <= len && comparfn(list[l], item) < 0) {
// left child comes before item, move hole down
list[idx] = list[l];
idx = l;
}
else {
// item comes before either child of this hole, leave item here
list[idx] = item;
break;
}
// idx points to the just vacated leaf node, bubble up item from there
while (idx > 1) {
var parentval = list[idx >> 1];
if (comparfn(item, parentval) < 0) {
list[idx] = parentval;
idx = idx >> 1;
}
else break;
}
list[idx] = item;

if (this._freeSpace) this._freeSpace(list, len);

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "qheap",
"version": "1.0.4",
"version": "1.0.5",
"description": "binary heap priority queue",
"license": "Apache-2.0",
"repository": {
Expand Down
11 changes: 9 additions & 2 deletions test/test-qheap.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ module.exports = {

'should sort the data': function(t) {
var i, data = [580, 253, 610, 176];
for (i=0; i<100000; i++) {
var nitems = 100000;
for (i=0; i<nitems; i++) {
data[i] = Math.random() * 1000 | 0;
this.cut.insert(data[i]);
}
Expand All @@ -75,7 +76,13 @@ module.exports = {
t.equal(this.cut.length, data.length);
// FIXME: this loop does not detect incorrect orderings...
var item = this.cut.remove();
while (this.cut.peek() !== undefined) { var x = this.cut.remove(); assert(x >= item); item = x; }
for (i=1; i<nitems; i++) {
var x = this.cut.remove();
//assert(x >= item, i + ": " + x + " should be >= " + item);
assert(x >= item);
item = x;
}
t.equal(this.cut.remove(), undefined);
t.equal(this.cut.length, 0);
t.done();
},
Expand Down

0 comments on commit c0d61ba

Please sign in to comment.