While researching binary tree implementations in JavaScript, I ran across Marijn Haverbeke's implementation from the first edition of Eloquent JavaScript. What I especially like about this implementation is the use of sequential representation of a binary tree.
I re-factored (using newer syntax, as the first edition is pre-ES6) and annotated:
1function BinaryHeap(compare) {2 this.content = [];3 this.compare = compare;4}56BinaryHeap.prototype = {7 push: function(element) {8 this.content.push(element);9 this.bubbleUp(this.content.length - 1);10 },1112 pop: function() {13 // Remove the first element from the tree,14 // and store it to remove it.15 let result = this.content.shift();1617 // When removing an element from a minHeap, 18 // we remove the last element, place it at 19 // the top, then allow it to sink down the20 // tree.21 if (this.content.length) {22 this.content.unshift(this.content.pop());23 this.sinkDown(0);24 }2526 return result;27 },2829 peek: function() {30 return this.content[0];31 },3233 size: function() {34 return this.content.length;35 },3637 bubbleUp: function(index) {38 // We now iterate through the tree, which is represented here as an array.39 // (See: https://www.geeksforgeeks.org/binary-tree-array-implementation/)40 //41 // To do so with a zero-indexed tree, which looks like this:42 //43 //. A(0) 44 //. / \45 //. B(1) C(2) 46 //. / \ \47 //. D(3) E(4) F(5) 48 //49 // Then, with parent P, then the left child is (2*P)+1, and the right 50 // child is (2*P)+2;51 //52 // Thus, the left child of B is:53 //54 // (2 * 1) + 1 = 3.55 //56 // While the right child is:57 // 58 // (2 * 1)+ 2 = 4.5960 while (index > 0) {61 let valueToInsert = this.content[index];6263 // To calculate the parent of a node, say the parent of node E64 // in the example above, we can do the following calculation:65 //66 // Math.floor((E + 1) / 2) - 1 = 67 // Math.floor(5 / 2) - 1 =68 // 2 - 1 = 169 // 70 // Similarly, to calculate the parent of F, we do the following:71 // 72 // Math.floor((F + 1) / 2) - 1 =73 // 3 - 1 = 27475 let parentNodeIndex = Math.floor((index + 1) / 2) - 1;7677 if (this.compare(78 this.content[parentNodeIndex], 79 this.content[index])) {80 81 // If the child node's value is (for a minHeap) greater than the parent node,82 // we're done, e.g. the parent node value must be less than83 break;84 85 } else {86 // Otherwise, we're going to need to swap the two values.87 // this.swap(this.content[index], this.content[parentNodeIndex])88 let tmp = this.content[parentNodeIndex];89 this.content[parentNodeIndex] = valueToInsert;90 this.content[index] = tmp;9192 // We're simply climbing up the binary tree93 // here, comparing each node with its parent.94 index = parentNodeIndex;95 }96 }97 },9899 sinkDown: function(index) {100 let length = this.content.length;101102 // we'll need to sink down the value at the index103 // the function is given.104 let valueToInsert = this.content[index];105106 // We now traverse the tree downwards.107 while (true) { 108 let rightChildIndex = (index + 1) * 2;109 let leftChildIndex = rightChildIndex - 1;110 let swapIndex = null;111112 // see if the left child exists.113 if (leftChildIndex < length) {114 // If so, if the left child's value is (for a minHeap) 115 // less than the index's value, prepare to swap the two.116 if (this.compare(117 this.content[leftChildIndex], 118 this.content[index])) 119 swapIndex = leftChildIndex;120 }121122 // see if the right child exists.123 if (rightChildIndex < length) {124 // If the right element is (for a minHeap) lower than the left element (and 125 // as a function of the logic, lower than the parent index) then swap with the parent126 // index.127 if (this.compare(this.content[rightChildIndex], 128 (swapIndex === null ? this.content[index] : this.content[leftChildIndex]))) {129 swapIndex = rightChildIndex;130 }131 }132133 // if there's nothing to swap, break.134 if (swapIndex === null) break;135136 // The value with either the parent, or other child.137 this.content[index] = this.content[swapIndex];138 this.content[swapIndex] = valueToInsert;139140 // Now, do the same process on the same value, but in the position of the141 // node with which we swapped.142 index = swapIndex;143 }144 }145 }146147var minHeap = new BinaryHeap((a, b) => a <= b);148([10, 3, 4, 8, 2, 9, 7, 1, 2, 6, 5].forEach((x) => minHeap.push(x)));149while (minHeap.content.length) console.log(minHeap.pop());150151var maxHeap = new BinaryHeap((a, b) => a >= b);152([10, 3, 4, 8, 2, 9, 7, 1, 2, 6, 5].forEach((x) => maxHeap.push(x)));153while (maxHeap.content.length) console.log(maxHeap.pop());
