-
Notifications
You must be signed in to change notification settings - Fork 929
/
Copy pathheap.js
103 lines (93 loc) · 2.53 KB
/
heap.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
/**
* Heap data structure a.k.a Priority Queue
*
* Used to get min or max values from a collection in constant time.
*
* @author Adrian Mejia <adrian@adrianmejia.com>
*/
class Heap {
constructor(comparator = (a, b) => a - b) {
this.array = [];
this.comparator = (i1, i2) => {
const value = comparator(this.array[i1], this.array[i2]);
if (Number.isNaN(value)) { throw new Error(`Comparator should evaluate to a number. Got ${value} when comparing ${this.array[i1]} with ${this.array[i2]}`); }
return value;
};
}
/**
* Insert element
* @runtime O(log n)
* @param {any} value
*/
add(value) {
this.array.push(value);
this.bubbleUp();
}
/**
* Retrieves, but does not remove, the head of this heap
* @runtime O(1)
*/
peek() {
return this.array[0];
}
/**
* Retrieves and removes the head of this heap, or returns null if this heap is empty.
* @runtime O(log n)
*/
remove(index = 0) {
if (!this.size) return null;
this.swap(index, this.size - 1); // swap with last
const value = this.array.pop(); // remove element
this.bubbleDown(index);
return value;
}
/**
* Returns the number of elements in this collection.
* @runtime O(1)
*/
get size() {
return this.array.length;
}
/**
* Move new element upwards on the heap, if it's out of order
* @runtime O(log n)
*/
bubbleUp() {
let index = this.size - 1;
const parent = (i) => Math.ceil(i / 2) - 1;
while (parent(index) >= 0 && this.comparator(parent(index), index) > 0) {
this.swap(parent(index), index);
index = parent(index);
}
}
/**
* After removal, moves element downwards on the heap, if it's out of order
* @runtime O(log n)
*/
bubbleDown(index = 0) {
let curr = index;
const left = (i) => 2 * i + 1;
const right = (i) => 2 * i + 2;
const getTopChild = (i) => (right(i) < this.size
&& this.comparator(left(i), right(i)) > 0 ? right(i) : left(i));
while (left(curr) < this.size && this.comparator(curr, getTopChild(curr)) > 0) {
const next = getTopChild(curr);
this.swap(curr, next);
curr = next;
}
}
/**
* Swap elements on the heap
* @runtime O(1)
* @param {number} i1 index 1
* @param {number} i2 index 2
*/
swap(i1, i2) {
[this.array[i1], this.array[i2]] = [this.array[i2], this.array[i1]];
}
}
// aliases
Heap.prototype.poll = Heap.prototype.remove;
Heap.prototype.offer = Heap.prototype.add;
Heap.prototype.element = Heap.prototype.peek;
module.exports = Heap;