Skip to content
File

Blob: src/worker/common/heap.ts

typescript93 lines
1/**
2 * Generic binary heap implementation with a user-provided comparator.
3 *
4 * Comparator contract:
5 * - cmp(a, b) < 0 => a has higher priority than b (a comes out first)
6 * - cmp(a, b) === 0 => a and b are equivalent in priority
7 * - cmp(a, b) > 0 => b has higher priority than a
8 *
9 * Usage:
10 * const heap = new BinaryHeap<number>((a, b) => a - b); // min-heap
11 * heap.push(3); heap.push(1); heap.push(2);
12 * console.log(heap.pop()); // 1
13 */
14export type Comparator<T> = (a: T, b: T) => number;
15 
16export class BinaryHeap<T> {
17 private a: T[];
18 private cmp: Comparator<T>;
19 
20 constructor(cmp: Comparator<T>, items?: Iterable<T>) {
21 this.cmp = cmp;
22 this.a = [];
23 if (items) {
24 for (const it of items) this.a.push(it);
25 if (this.a.length > 1) this.heapify();
26 }
27 }
28 
29 size(): number {
30 return this.a.length;
31 }
32 
33 isEmpty(): boolean {
34 return this.a.length === 0;
35 }
36 
37 peek(): T | undefined {
38 return this.a[0];
39 }
40 
41 push(v: T): void {
42 this.a.push(v);
43 this.siftUp(this.a.length - 1);
44 }
45 
46 pop(): T | undefined {
47 if (this.a.length === 0) return undefined;
48 const top = this.a[0];
49 const last = this.a.pop()!;
50 if (this.a.length > 0) {
51 this.a[0] = last;
52 this.siftDown(0);
53 }
54 return top;
55 }
56 
57 private heapify(): void {
58 for (let i = Math.floor(this.a.length / 2) - 1; i >= 0; i--) this.siftDown(i);
59 }
60 
61 private siftDown(i: number): void {
62 const n = this.a.length;
63 while (true) {
64 const l = i * 2 + 1;
65 const r = l + 1;
66 let best = i;
67 if (l < n && this.cmp(this.a[l], this.a[best]) < 0) best = l;
68 if (r < n && this.cmp(this.a[r], this.a[best]) < 0) best = r;
69 if (best === i) break;
70 this.swap(i, best);
71 i = best;
72 }
73 }
74 
75 private siftUp(i: number): void {
76 while (i > 0) {
77 const p = Math.floor((i - 1) / 2);
78 if (this.cmp(this.a[i], this.a[p]) < 0) {
79 this.swap(i, p);
80 i = p;
81 } else {
82 break;
83 }
84 }
85 }
86 
87 private swap(i: number, j: number) {
88 const t = this.a[i];
89 this.a[i] = this.a[j];
90 this.a[j] = t;
91 }
92}