Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions docs/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export const examples: {
readonly diffTree: 'examples/treeDiff.ts';
readonly applyTreeDiff: 'examples/treeDiff.ts';
readonly UnionFind: 'examples/graph.ts';
readonly BinaryHeap: 'examples/binaryHeap.ts';
};
readonly performance: {
readonly debounce: 'examples/requestDedup.ts';
Expand Down Expand Up @@ -2929,6 +2930,19 @@ export function groupBy<T>(
key: keyof T | ((item: T) => string)
): Record<string, T[]>;

/**
* Binary heap (priority queue) with custom comparator.
* Use for: A*/Dijkstra, schedulers, real-time queues.
* Import: data/binaryHeap.ts
*/
export class BinaryHeap<T> {
constructor(compare: (a: T, b: T) => number, items?: Iterable<T>);
readonly size: number;
peek(): T | undefined;
push(value: T): void;
pop(): T | undefined;
}

/**
* Disjoint Set Union (Union-Find) with path compression and union by size.
* Use for: connectivity queries, Kruskal MST, clustering.
Expand Down
8 changes: 8 additions & 0 deletions examples/binaryHeap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { BinaryHeap } from '../src/index.js';

const heap = new BinaryHeap<number>((a, b) => a - b, [5, 1, 4]);
heap.push(3);
console.log('peek', heap.peek());
console.log('pop', heap.pop());
console.log('pop', heap.pop());

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
{
"name": "bundle",
"path": "dist/index.js",
"limit": "41 KB"
"limit": "42 KB"
}
]
}
81 changes: 81 additions & 0 deletions src/data/binaryHeap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/**
* Minimal binary heap (priority queue) with custom comparator.
* Useful for: A* / Dijkstra open sets, schedulers, simulation queues.
*/
export class BinaryHeap<T> {
private data: T[] = [];
constructor(private compare: (a: T, b: T) => number, items?: Iterable<T>) {
if (items) {
for (const it of items) this.data.push(it);
this.heapify();
}
}

get size(): number {
return this.data.length;
}

peek(): T | undefined {
return this.data[0];
}

push(value: T): void {
this.data.push(value);
this.bubbleUp(this.data.length - 1);
}

pop(): T | undefined {
const n = this.data.length;
if (n === 0) return undefined;
const top = this.data[0];
const last = this.data.pop() as T;
if (n > 1) {
this.data[0] = last;
this.bubbleDown(0);
}
return top;
}

private heapify(): void {
for (let i = Math.floor(this.data.length / 2) - 1; i >= 0; i -= 1) {
this.bubbleDown(i);
}
}

private bubbleUp(i: number): void {
while (i > 0) {
const p = (i - 1) >> 1;
if (this.compare(this.data[i], this.data[p]) < 0) {
this.swap(i, p);
i = p;
} else {
break;
}
}
}

private bubbleDown(i: number): void {
const n = this.data.length;
let idx = i;
let moved = true;
while (moved) {
moved = false;
const l = idx * 2 + 1;
const r = l + 1;
let smallest = idx;
if (l < n && this.compare(this.data[l], this.data[smallest]) < 0) smallest = l;
if (r < n && this.compare(this.data[r], this.data[smallest]) < 0) smallest = r;
if (smallest !== idx) {
this.swap(idx, smallest);
idx = smallest;
moved = true;
}
}
}

private swap(i: number, j: number): void {
const tmp = this.data[i];
this.data[i] = this.data[j];
this.data[j] = tmp;
}
}
7 changes: 7 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ export const examples = {
diffTree: 'examples/treeDiff.ts',
applyTreeDiff: 'examples/treeDiff.ts',
UnionFind: 'examples/graph.ts',
BinaryHeap: 'examples/binaryHeap.ts',
},
performance: {
debounce: 'examples/requestDedup.ts',
Expand Down Expand Up @@ -1026,6 +1027,12 @@ export { diffTree, applyTreeDiff } from './data/treeDiff.js';
* Example file: examples/graph.ts
*/
export { UnionFind } from './data/unionFind.js';
/**
* Binary heap (priority queue) with custom comparator.
*
* Example file: examples/binaryHeap.ts
*/
export { BinaryHeap } from './data/binaryHeap.js';

export type {
TreeNode,
Expand Down
26 changes: 26 additions & 0 deletions tests/binaryHeap.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { describe, it, expect } from 'vitest';
import { BinaryHeap } from '../src/index.js';

describe('BinaryHeap', () => {
it('orders items by comparator', () => {
const heap = new BinaryHeap<number>((a, b) => a - b, [5, 1, 3, 4, 2]);
expect(heap.size).toBe(5);
expect(heap.peek()).toBe(1);
const popped: number[] = [];
while (heap.size) popped.push(heap.pop()!);
expect(popped).toEqual([1, 2, 3, 4, 5]);
});

it('supports pushing and popping', () => {
const heap = new BinaryHeap<number>((a, b) => a - b);
heap.push(10);
heap.push(5);
heap.push(7);
expect(heap.peek()).toBe(5);
expect(heap.pop()).toBe(5);
expect(heap.pop()).toBe(7);
expect(heap.pop()).toBe(10);
expect(heap.pop()).toBeUndefined();
});
});

1 change: 1 addition & 0 deletions tests/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ describe('package entry point', () => {
| 'diffTree'
| 'applyTreeDiff'
| 'UnionFind'
| 'BinaryHeap'
>();

expectTypeOf<ExampleName<'search'>>().toEqualTypeOf<
Expand Down