diff --git a/README.md b/README.md index ff18379..df95950 100644 --- a/README.md +++ b/README.md @@ -30,11 +30,17 @@ import { BiDirectionalMap, LRUCache, } from 'jsr:@mskr/data-structures'; -// or if you want to use it with esm.sh +``` + +### Using directly without installing the package with esm.sh + +```typescript // import * as ds from 'https://esm.sh/jsr/@mskr/data-structures'; // import { ... } from 'https://esm.sh/jsr/@mskr/data-structures'; ``` +[**_Demo_**](https://codepen.io/mandy8055/pen/ZYzBpjL) + ## Available Data Structures and their detailed documentations - [LinkedList](./docs/linked-list.md): Singly linked list implementation diff --git a/docs/binary-heap.md b/docs/binary-heap.md index e3e5107..e1f1742 100644 --- a/docs/binary-heap.md +++ b/docs/binary-heap.md @@ -105,6 +105,19 @@ minHeap.insert({ name: 'Charlie', age: 30 }); console.log(minHeap.peek()); // { name: "Bob", age: 20 } ``` +### Heap Construction + +```typescript +// Create a heap with initial elements O(n) +const minHeap = new MinHeap(null, [5, 3, 8, 1, 7]); +console.log(minHeap.peek()); // 1 (minimum element) +console.log(minHeap.size); // 5 +// Similarly for MaxHeap +const maxHeap = new MaxHeap(null, [5, 3, 8, 1]); +console.log(maxHeap.peek()); // 8 +console.log(maxHeap.size); // 4 +``` + ### Type-Safe Comparable Objects ```typescript @@ -140,6 +153,7 @@ try { - Peek: O(1) - Contains: O(n) - Space complexity: O(n) +- Build Heap from Array: O(n) ## Implementation Details @@ -149,6 +163,10 @@ The heap is implemented as a complete binary tree stored in an array, where for - Right child is at index: 2i + 2 - Parent is at index: floor((i-1)/2) +## Efficient heap Initialization + +When creating a heap with an initial array of elements, the construction is optimized to use an O(n) algorithm. This means that initializing a heap with an array is significantly more efficient than inserting elements one by one(takes O(n log n)), providing a performant way to create heaps from existing collections. + ### Key Features 1. Generic type support with type safety diff --git a/docs/priority-queue.md b/docs/priority-queue.md index 57dd7f9..6ef7e88 100644 --- a/docs/priority-queue.md +++ b/docs/priority-queue.md @@ -165,6 +165,7 @@ try { - toArray: O(n) - toSortedArray: O(n log n) - Space complexity: O(n) +- Initialize Priority Queue with array: O(n) ## Implementation Details @@ -179,7 +180,7 @@ The priority queue is implemented using a binary min-heap where: 1. Generic type support 2. Customizable priority ordering through comparator function -3. Optional initialization with existing values +3. Optional initialization with existing values in O(n) 4. Both heap-ordered and priority-ordered array conversions 5. Efficient priority-based operations 6. Iterator implementation for collection processing diff --git a/src/core/binary-heap.ts b/src/core/binary-heap.ts index 3745947..0498581 100644 --- a/src/core/binary-heap.ts +++ b/src/core/binary-heap.ts @@ -31,6 +31,17 @@ import { compareNumbers, compareStrings } from '../utils/index.ts'; * console.log(minHeap.peek()); // 3 * console.log(minHeap.remove()); // 3 * ``` + * + * @example + * ```typescript + * const minHeap = new MinHeap(null, [5, 3, 8, 1]); + * console.log(minHeap.peek()); // 1 + * console.log(minHeap.size); // 4 + * // Similarly for MaxHeap + * const maxHeap = new MaxHeap(null, [5, 3, 8, 1]); + * console.log(maxHeap.peek()); // 8 + * console.log(maxHeap.size); // 4 + * ``` */ export abstract class BinaryHeap implements Iterable { /** @@ -47,10 +58,15 @@ export abstract class BinaryHeap implements Iterable { /** * Creates an empty binary heap * @param comparator Optional custom comparison function + * @param initial Optional array of elements to initialize the heap */ - constructor(comparator?: (a: T, b: T) => number) { + constructor(comparator?: (a: T, b: T) => number, initial?: T[]) { this.heap = []; this.compare = comparator || this.getDefaultComparator(); + + if (initial && initial.length > 0) { + this.buildHeap(initial); + } } /** @@ -180,6 +196,19 @@ export abstract class BinaryHeap implements Iterable { return Math.floor((index - 1) / 2); } + /** + * @ignore + * Efficiently builds a heap from an initial array in O(n) time + * @protected + * @param initial Array of elements to build the heap from + */ + protected buildHeap(initial: T[]): void { + this.heap = [...initial]; + for (let i = Math.floor(this.heap.length / 2) - 1; i >= 0; i--) { + this.siftDown(i); + } + } + /** * @ignore * Gets the left child index for a given parent index @@ -216,18 +245,7 @@ export abstract class BinaryHeap implements Iterable { /** * MinHeap implementation where the root is always the minimum element. * - * All operations inherited from {@link BinaryHeap} are available: - * @augments BinaryHeap - * - * Methods available from BinaryHeap: - * - {@link BinaryHeap#size} - Returns the number of elements in the heap - * - {@link BinaryHeap#isEmpty} - Checks if the heap is empty - * - {@link BinaryHeap#peek} - Returns the minimum element without removing it - * - {@link BinaryHeap#insert} - Inserts a new element into the heap - * - {@link BinaryHeap#remove} - Removes and returns the minimum element - * - {@link BinaryHeap#contains} - Checks if an element exists in the heap - * - {@link BinaryHeap#clear} - Removes all elements from the heap - * - {@link BinaryHeap#toArray} - Converts the heap to an array + * All operations inherited from {@link BinaryHeap} are available. * * @template T The type of elements stored in the heap * @@ -311,18 +329,7 @@ export class MinHeap extends BinaryHeap { /** * MaxHeap implementation where the root is always the maximum element. * - * All operations inherited from {@link BinaryHeap} are available: - * @augments BinaryHeap - * - * Methods available from BinaryHeap: - * - {@link BinaryHeap#size} - Returns the number of elements in the heap - * - {@link BinaryHeap#isEmpty} - Checks if the heap is empty - * - {@link BinaryHeap#peek} - Returns the maximum element without removing it - * - {@link BinaryHeap#insert} - Inserts a new element into the heap - * - {@link BinaryHeap#remove} - Removes and returns the maximum element - * - {@link BinaryHeap#contains} - Checks if an element exists in the heap - * - {@link BinaryHeap#clear} - Removes all elements from the heap - * - {@link BinaryHeap#toArray} - Converts the heap to an array + * All operations inherited from {@link BinaryHeap} are available. * * @template T The type of elements stored in the heap * diff --git a/src/core/priority-queue.ts b/src/core/priority-queue.ts index 9af16eb..28d2379 100644 --- a/src/core/priority-queue.ts +++ b/src/core/priority-queue.ts @@ -60,13 +60,7 @@ export class PriorityQueue implements Iterable { */ initial?: T[]; }) { - this.heap = new MinHeap(options?.comparator); - - if (options?.initial) { - for (const value of options.initial) { - this.enqueue(value); - } - } + this.heap = new MinHeap(options?.comparator, options?.initial); } /** diff --git a/src/tests/binary-heap.test.ts b/src/tests/binary-heap.test.ts index 85160dd..7909938 100644 --- a/src/tests/binary-heap.test.ts +++ b/src/tests/binary-heap.test.ts @@ -231,3 +231,113 @@ Deno.test('BinaryHeap - Coverage Enhancement Tests', async (t) => { assertEquals(elements, [3, 7, 8, 9, 10]); }); }); + +Deno.test('BinaryHeap - BuildHeap Method', async (t) => { + await t.step('should build MinHeap from initial array efficiently', () => { + const initialArray = [5, 3, 7, 1, 4]; + const heap = new MinHeap(undefined, initialArray); + + // Verify size + assertEquals(heap.size, 5); + + // Verify heap property (root should be minimum) + assertEquals(heap.peek(), 1); + + // Verify contents + const elements = []; + while (!heap.isEmpty()) { + elements.push(heap.remove()); + } + assertEquals(elements, [1, 3, 4, 5, 7]); + }); + + await t.step('should build MaxHeap from initial array efficiently', () => { + const initialArray = [5, 3, 7, 1, 4]; + const heap = new MaxHeap(undefined, initialArray); + + // Verify size + assertEquals(heap.size, 5); + + // Verify heap property (root should be maximum) + assertEquals(heap.peek(), 7); + + // Verify contents + const elements = []; + while (!heap.isEmpty()) { + elements.push(heap.remove()); + } + assertEquals(elements, [7, 5, 4, 3, 1]); + }); + + await t.step('should build heap with custom comparator', () => { + interface Person { + name: string; + priority: number; + } + + const people: Person[] = [ + { name: 'Alice', priority: 3 }, + { name: 'Bob', priority: 1 }, + { name: 'Charlie', priority: 2 }, + ]; + + const heap = new MinHeap((a, b) => a.priority - b.priority, people); + + // Verify size + assertEquals(heap.size, 3); + + // Verify heap property + assertEquals(heap.peek().name, 'Bob'); + + // Remove elements and verify order + const removedPeople = []; + while (!heap.isEmpty()) { + removedPeople.push(heap.remove()); + } + assertEquals( + removedPeople.map((p) => p.name), + ['Bob', 'Charlie', 'Alice'], + ); + }); + + await t.step('should handle empty initial array', () => { + const heap = new MinHeap(undefined, []); + + // Verify empty heap + assertEquals(heap.size, 0); + assertEquals(heap.isEmpty(), true); + assertThrows(() => heap.peek(), EmptyStructureError); + }); + + await t.step('should handle single element array', () => { + const heap = new MinHeap(undefined, [42]); + + // Verify heap properties + assertEquals(heap.size, 1); + assertEquals(heap.peek(), 42); + assertEquals(heap.remove(), 42); + assertEquals(heap.isEmpty(), true); + }); + + await t.step('should work with Comparable objects', () => { + const items = [ + new ComparableItem(5), + new ComparableItem(3), + new ComparableItem(7), + new ComparableItem(1), + new ComparableItem(4), + ]; + + const heap = new MinHeap(undefined, items); + + // Verify heap property + assertEquals(heap.peek().getValue(), 1); + + // Remove and verify order + const values = []; + while (!heap.isEmpty()) { + values.push(heap.remove().getValue()); + } + assertEquals(values, [1, 3, 4, 5, 7]); + }); +});