From 6539e50598796a562534a0fc58a024872b37edaf Mon Sep 17 00:00:00 2001 From: Revone Date: Wed, 31 Jan 2024 10:04:21 +0800 Subject: [PATCH] feat: Provide custom toElementFn in Heap configuration options to transform user raw data into a specified element type within a single loop. --- src/data-structures/heap/heap.ts | 19 ++++++--- src/data-structures/heap/max-heap.ts | 16 ++++--- src/data-structures/heap/min-heap.ts | 4 +- .../priority-queue/max-priority-queue.ts | 29 ++++++------- .../priority-queue/min-priority-queue.ts | 19 ++------- .../priority-queue/priority-queue.ts | 6 +-- .../binary-tree/binary-tree.ts | 2 +- src/types/data-structures/heap/heap.ts | 5 ++- .../priority-queue/priority-queue.ts | 2 +- test/unit/data-structures/heap/heap.test.ts | 42 +++++++++++++++++++ .../priority-queue/max-priority-queue.test.ts | 42 +++++++++++++++++++ 11 files changed, 134 insertions(+), 52 deletions(-) diff --git a/src/data-structures/heap/heap.ts b/src/data-structures/heap/heap.ts index 6489812..f596ba5 100644 --- a/src/data-structures/heap/heap.ts +++ b/src/data-structures/heap/heap.ts @@ -20,7 +20,7 @@ import { IterableElementBase } from '../base'; * 7. Efficient Sorting Algorithms: For example, heap sort. Heap sort uses the properties of a heap to sort elements. * 8. Graph Algorithms: Such as Dijkstra's shortest path algorithm and Prime's minimum-spanning tree algorithm, which use heaps to improve performance. */ -export class Heap extends IterableElementBase { +export class Heap extends IterableElementBase { /** * The constructor initializes a heap data structure with optional elements and options. * @param elements - The `elements` parameter is an iterable object that contains the initial @@ -34,17 +34,18 @@ export class Heap extends IterableElementBase { * The comparator function is used to determine the * order of elements in the heap. */ - constructor(elements: Iterable = [], options?: HeapOptions) { + constructor(elements: Iterable | Iterable = [], options?: HeapOptions) { super(); - if (options) { - const { comparator } = options; + const { comparator, toElementFn } = options; if (comparator) this._comparator = comparator; + if (toElementFn) this._toElementFn = toElementFn; } if (elements) { for (const el of elements) { - this.add(el); + if (this._toElementFn) this.add(this._toElementFn(el as R)); + else this.add(el as E); } } } @@ -59,6 +60,12 @@ export class Heap extends IterableElementBase { return this._elements; } + protected _toElementFn?: (rawElement: R) => E; + + get toElementFn() { + return this._toElementFn; + } + /** * Get the size (number of elements) of the heap. */ @@ -80,7 +87,7 @@ export class Heap extends IterableElementBase { * @param elements * @param options */ - static heapify(elements: Iterable, options: HeapOptions): Heap { + static heapify(elements: Iterable, options: HeapOptions): Heap { return new Heap(elements, options); } diff --git a/src/data-structures/heap/max-heap.ts b/src/data-structures/heap/max-heap.ts index ebb6eb6..9a2621f 100644 --- a/src/data-structures/heap/max-heap.ts +++ b/src/data-structures/heap/max-heap.ts @@ -16,12 +16,11 @@ import { Heap } from './heap'; * 5. Managing Dynamic Data Sets: Heaps effectively manage dynamic data sets, especially when frequent access to the largest or smallest elements is required. * 6. Non-linear Search: While a heap allows rapid access to its largest or smallest element, it is less efficient for other operations, such as searching for a specific element, as it is not designed for these tasks. * 7. Efficient Sorting Algorithms: For example, heap sort. Heap sort uses the properties of a heap to sort elements. - * 8. Graph Algorithms: Such as Dijkstra's shortest path algorithm and Prim's minimum spanning tree algorithm, which use heaps to improve performance. + * 8. Graph Algorithms: Such as Dijkstra's shortest path algorithm and Prim's minimum-spanning tree algorithm, which use heaps to improve performance. */ -export class MaxHeap extends Heap { - constructor( - elements: Iterable = [], - options: HeapOptions = { +export class MaxHeap extends Heap { + constructor(elements: Iterable | Iterable = [], options?: HeapOptions) { + super(elements, { comparator: (a: E, b: E): number => { if (typeof a === 'object' || typeof b === 'object') { throw TypeError( @@ -31,9 +30,8 @@ export class MaxHeap extends Heap { if (a < b) return 1; if (a > b) return -1; return 0; - } - } - ) { - super(elements, options); + }, + ...options + }); } } diff --git a/src/data-structures/heap/min-heap.ts b/src/data-structures/heap/min-heap.ts index 1f1d2be..f76713a 100644 --- a/src/data-structures/heap/min-heap.ts +++ b/src/data-structures/heap/min-heap.ts @@ -18,8 +18,8 @@ import { Heap } from './heap'; * 7. Efficient Sorting Algorithms: For example, heap sort. Heap sort uses the properties of a heap to sort elements. * 8. Graph Algorithms: Such as Dijkstra's shortest path algorithm and Prim's minimum spanning tree algorithm, which use heaps to improve performance. */ -export class MinHeap extends Heap { - constructor(elements: Iterable = [], options?: HeapOptions) { +export class MinHeap extends Heap { + constructor(elements: Iterable | Iterable = [], options?: HeapOptions) { super(elements, options); } } diff --git a/src/data-structures/priority-queue/max-priority-queue.ts b/src/data-structures/priority-queue/max-priority-queue.ts index ca1c45c..05b52dd 100644 --- a/src/data-structures/priority-queue/max-priority-queue.ts +++ b/src/data-structures/priority-queue/max-priority-queue.ts @@ -8,7 +8,7 @@ import type { PriorityQueueOptions } from '../../types'; import { PriorityQueue } from './priority-queue'; -export class MaxPriorityQueue extends PriorityQueue { +export class MaxPriorityQueue extends PriorityQueue { /** * The constructor initializes a PriorityQueue with optional elements and options, including a * comparator function. @@ -16,21 +16,22 @@ export class MaxPriorityQueue extends PriorityQueue { * elements to be added to the priority queue. It is optional and defaults to an empty array if not * provided. * @param options - The `options` parameter is an object that contains additional configuration - * options for the priority queue. In this case, it has a property called `comparator` which is a + * options for the priority queue. In this case, it has a property called `comparator,` which is a * function used to compare elements in the priority queue. */ - constructor( - elements: Iterable = [], - options: PriorityQueueOptions = { - comparator: (a: E, b: E) => { - if (!(typeof a === 'number' && typeof b === 'number')) { - throw new Error('The a, b params of compare function must be number'); - } else { - return b - a; + constructor(elements: Iterable | Iterable = [], options?: PriorityQueueOptions) { + super(elements, { + comparator: (a: E, b: E): number => { + if (typeof a === 'object' || typeof b === 'object') { + throw TypeError( + `When comparing object types, a custom comparator must be defined in the constructor's options parameter.` + ); } - } - } - ) { - super(elements, options); + if (a < b) return 1; + if (a > b) return -1; + return 0; + }, + ...options + }); } } diff --git a/src/data-structures/priority-queue/min-priority-queue.ts b/src/data-structures/priority-queue/min-priority-queue.ts index 04a25c4..33edf7d 100644 --- a/src/data-structures/priority-queue/min-priority-queue.ts +++ b/src/data-structures/priority-queue/min-priority-queue.ts @@ -8,7 +8,7 @@ import type { PriorityQueueOptions } from '../../types'; import { PriorityQueue } from './priority-queue'; -export class MinPriorityQueue extends PriorityQueue { +export class MinPriorityQueue extends PriorityQueue { /** * The constructor initializes a PriorityQueue with optional elements and options, including a * comparator function. @@ -16,22 +16,11 @@ export class MinPriorityQueue extends PriorityQueue { * elements to be added to the priority queue. It is optional and defaults to an empty array if not * provided. * @param options - The `options` parameter is an object that contains additional configuration - * options for the priority queue. In this case, it has a property called `comparator` which is a + * options for the priority queue. In this case, it has a property called `comparator,` which is a * function used to compare elements in the priority queue. The `comparator` function takes two - * parameters `a` and `b`, + * parameters `a` and `b` */ - constructor( - elements: Iterable = [], - options: PriorityQueueOptions = { - comparator: (a: E, b: E) => { - if (!(typeof a === 'number' && typeof b === 'number')) { - throw new Error('The a, b params of compare function must be number'); - } else { - return a - b; - } - } - } - ) { + constructor(elements: Iterable | Iterable = [], options?: PriorityQueueOptions) { super(elements, options); } } diff --git a/src/data-structures/priority-queue/priority-queue.ts b/src/data-structures/priority-queue/priority-queue.ts index c871ee2..11c68a8 100644 --- a/src/data-structures/priority-queue/priority-queue.ts +++ b/src/data-structures/priority-queue/priority-queue.ts @@ -16,16 +16,16 @@ import { Heap } from '../heap'; * 5. Huffman Coding: Used to select the smallest node combination when constructing a Huffman tree. * 6. Kth Largest Element in a Data Stream: Used to maintain a min-heap of size K for quickly finding the Kth largest element in stream data */ -export class PriorityQueue extends Heap { +export class PriorityQueue extends Heap { /** * The constructor initializes a priority queue with optional elements and options. * @param elements - The `elements` parameter is an iterable object that contains the initial - * elements to be added to the priority queue. It is an optional parameter and if not provided, the + * elements to be added to the priority queue. It is an optional parameter, and if not provided, the * priority queue will be initialized as empty. * @param [options] - The `options` parameter is an optional object that can be used to customize the * behavior of the priority queue. It can contain the following properties: */ - constructor(elements: Iterable = [], options?: PriorityQueueOptions) { + constructor(elements: Iterable | Iterable = [], options?: PriorityQueueOptions) { super(elements, options); } } diff --git a/src/types/data-structures/binary-tree/binary-tree.ts b/src/types/data-structures/binary-tree/binary-tree.ts index f78855d..4be2252 100644 --- a/src/types/data-structures/binary-tree/binary-tree.ts +++ b/src/types/data-structures/binary-tree/binary-tree.ts @@ -6,6 +6,6 @@ export type BinaryTreeNodeNested = BinaryTreeNode> = BinaryTree>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> export type BinaryTreeOptions = { - iterationType?: IterationType + iterationType?: IterationType; toEntryFn?: (rawElement: R) => BTNEntry; } diff --git a/src/types/data-structures/heap/heap.ts b/src/types/data-structures/heap/heap.ts index 4631285..0469867 100644 --- a/src/types/data-structures/heap/heap.ts +++ b/src/types/data-structures/heap/heap.ts @@ -1,3 +1,6 @@ import { Comparator } from '../../common'; -export type HeapOptions = { comparator?: Comparator }; +export type HeapOptions = { + comparator?: Comparator; + toElementFn?: (rawElement: R) => E; +}; diff --git a/src/types/data-structures/priority-queue/priority-queue.ts b/src/types/data-structures/priority-queue/priority-queue.ts index 44718a5..8168fad 100644 --- a/src/types/data-structures/priority-queue/priority-queue.ts +++ b/src/types/data-structures/priority-queue/priority-queue.ts @@ -1,3 +1,3 @@ import { HeapOptions } from '../heap'; -export type PriorityQueueOptions = HeapOptions & {}; +export type PriorityQueueOptions = HeapOptions & {}; diff --git a/test/unit/data-structures/heap/heap.test.ts b/test/unit/data-structures/heap/heap.test.ts index 3cb16ed..78beeef 100644 --- a/test/unit/data-structures/heap/heap.test.ts +++ b/test/unit/data-structures/heap/heap.test.ts @@ -87,6 +87,48 @@ describe('Heap Operation Test', () => { maxI++; } }); + + it('should object heap', () => { + const heap = new Heap<{ rawItem: { id: number } }>( + [ + { rawItem: { id: 4 } }, + { rawItem: { id: 8 } }, + { rawItem: { id: 6 } }, + { rawItem: { id: 7 } }, + { rawItem: { id: 1 } }, + { rawItem: { id: 3 } }, + { rawItem: { id: 5 } } + ], + { comparator: (a, b) => a.rawItem.id - b.rawItem.id } + ); + + expect([...heap.sort()]).toEqual([ + { rawItem: { id: 1 } }, + { rawItem: { id: 3 } }, + { rawItem: { id: 4 } }, + { rawItem: { id: 5 } }, + { rawItem: { id: 6 } }, + { rawItem: { id: 7 } }, + { rawItem: { id: 8 } } + ]); + }); + + it('should toElementFn', () => { + const heap = new Heap( + [ + { rawItem: { id: 4 } }, + { rawItem: { id: 8 } }, + { rawItem: { id: 6 } }, + { rawItem: { id: 7 } }, + { rawItem: { id: 1 } }, + { rawItem: { id: 3 } }, + { rawItem: { id: 5 } } + ], + { toElementFn: rawElement => rawElement.rawItem.id } + ); + + expect([...heap.sort()]).toEqual([1, 3, 4, 5, 6, 7, 8]); + }); }); describe('FibonacciHeap', () => { diff --git a/test/unit/data-structures/priority-queue/max-priority-queue.test.ts b/test/unit/data-structures/priority-queue/max-priority-queue.test.ts index 50a0e9a..8deba88 100644 --- a/test/unit/data-structures/priority-queue/max-priority-queue.test.ts +++ b/test/unit/data-structures/priority-queue/max-priority-queue.test.ts @@ -71,4 +71,46 @@ describe('MaxPriorityQueue Operation Test', () => { expect(maxPQ.poll()?.keyA).toBe(3); expect(maxPQ.poll()?.keyA).toBe(1); }); + + it('should object priority queue', () => { + const maxPQ = new MaxPriorityQueue<{ rawItem: { id: number } }>( + [ + { rawItem: { id: 4 } }, + { rawItem: { id: 8 } }, + { rawItem: { id: 6 } }, + { rawItem: { id: 7 } }, + { rawItem: { id: 1 } }, + { rawItem: { id: 3 } }, + { rawItem: { id: 5 } } + ], + { comparator: (a, b) => b.rawItem.id - a.rawItem.id } + ); + + expect([...maxPQ.sort()]).toEqual([ + { rawItem: { id: 8 } }, + { rawItem: { id: 7 } }, + { rawItem: { id: 6 } }, + { rawItem: { id: 5 } }, + { rawItem: { id: 4 } }, + { rawItem: { id: 3 } }, + { rawItem: { id: 1 } } + ]); + }); + + it('should toElementFn', () => { + const maxPQ = new MaxPriorityQueue( + [ + { rawItem: { id: 4 } }, + { rawItem: { id: 8 } }, + { rawItem: { id: 6 } }, + { rawItem: { id: 7 } }, + { rawItem: { id: 1 } }, + { rawItem: { id: 3 } }, + { rawItem: { id: 5 } } + ], + { toElementFn: rawElement => rawElement.rawItem.id } + ); + + expect([...maxPQ.sort()]).toEqual([8, 7, 6, 5, 4, 3, 1]); + }); });