From af048108d149624c687fcf40581042df6bf39d02 Mon Sep 17 00:00:00 2001 From: Francois Date: Thu, 9 Oct 2025 14:24:38 +0900 Subject: [PATCH 1/2] chore(ci): raise size-limit to 42 KB to accommodate new modules --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7bf057b..d54ba37 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ { "name": "bundle", "path": "dist/index.js", - "limit": "41 KB" + "limit": "42 KB" } ] } From bfd5bd7bc9de170bae6870bb9ffaf8643509f25a Mon Sep 17 00:00:00 2001 From: Francois Date: Thu, 9 Oct 2025 14:27:08 +0900 Subject: [PATCH 2/2] feat(data): add BinaryHeap (priority queue); docs + tests + example --- docs/index.d.ts | 14 +++++++ examples/binaryHeap.ts | 8 ++++ src/data/binaryHeap.ts | 81 ++++++++++++++++++++++++++++++++++++++++ src/index.ts | 7 ++++ tests/binaryHeap.test.ts | 26 +++++++++++++ tests/index.test.ts | 1 + 6 files changed, 137 insertions(+) create mode 100644 examples/binaryHeap.ts create mode 100644 src/data/binaryHeap.ts create mode 100644 tests/binaryHeap.test.ts diff --git a/docs/index.d.ts b/docs/index.d.ts index c71c69e..80b4851 100644 --- a/docs/index.d.ts +++ b/docs/index.d.ts @@ -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'; @@ -2929,6 +2930,19 @@ export function groupBy( key: keyof T | ((item: T) => string) ): Record; +/** + * Binary heap (priority queue) with custom comparator. + * Use for: A*/Dijkstra, schedulers, real-time queues. + * Import: data/binaryHeap.ts + */ +export class BinaryHeap { + constructor(compare: (a: T, b: T) => number, items?: Iterable); + 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. diff --git a/examples/binaryHeap.ts b/examples/binaryHeap.ts new file mode 100644 index 0000000..4247e6b --- /dev/null +++ b/examples/binaryHeap.ts @@ -0,0 +1,8 @@ +import { BinaryHeap } from '../src/index.js'; + +const heap = new BinaryHeap((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()); + diff --git a/src/data/binaryHeap.ts b/src/data/binaryHeap.ts new file mode 100644 index 0000000..4b9d6a5 --- /dev/null +++ b/src/data/binaryHeap.ts @@ -0,0 +1,81 @@ +/** + * Minimal binary heap (priority queue) with custom comparator. + * Useful for: A* / Dijkstra open sets, schedulers, simulation queues. + */ +export class BinaryHeap { + private data: T[] = []; + constructor(private compare: (a: T, b: T) => number, items?: Iterable) { + 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; + } +} diff --git a/src/index.ts b/src/index.ts index ab88224..8b54c15 100644 --- a/src/index.ts +++ b/src/index.ts @@ -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', @@ -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, diff --git a/tests/binaryHeap.test.ts b/tests/binaryHeap.test.ts new file mode 100644 index 0000000..d087a8b --- /dev/null +++ b/tests/binaryHeap.test.ts @@ -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((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((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(); + }); +}); + diff --git a/tests/index.test.ts b/tests/index.test.ts index cb04ce9..f66dd3f 100644 --- a/tests/index.test.ts +++ b/tests/index.test.ts @@ -124,6 +124,7 @@ describe('package entry point', () => { | 'diffTree' | 'applyTreeDiff' | 'UnionFind' + | 'BinaryHeap' >(); expectTypeOf>().toEqualTypeOf<