feat: Provide custom toElementFn in Heap configuration options to transform user raw data into a specified element type within a single loop.

This commit is contained in:
Revone 2024-01-31 10:04:21 +08:00
parent 8051850331
commit 6539e50598
11 changed files with 134 additions and 52 deletions

View file

@ -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<E = any> extends IterableElementBase<E> {
export class Heap<E = any, R = any> extends IterableElementBase<E> {
/**
* 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<E = any> extends IterableElementBase<E> {
* The comparator function is used to determine the
* order of elements in the heap.
*/
constructor(elements: Iterable<E> = [], options?: HeapOptions<E>) {
constructor(elements: Iterable<E> | Iterable<R> = [], options?: HeapOptions<E, R>) {
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<E = any> extends IterableElementBase<E> {
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<E = any> extends IterableElementBase<E> {
* @param elements
* @param options
*/
static heapify<E>(elements: Iterable<E>, options: HeapOptions<E>): Heap<E> {
static heapify<E = any, R = any>(elements: Iterable<E>, options: HeapOptions<E, R>): Heap<E> {
return new Heap<E>(elements, options);
}

View file

@ -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<E = any> extends Heap<E> {
constructor(
elements: Iterable<E> = [],
options: HeapOptions<E> = {
export class MaxHeap<E = any, R = any> extends Heap<E> {
constructor(elements: Iterable<E> | Iterable<R> = [], options?: HeapOptions<E, R>) {
super(elements, {
comparator: (a: E, b: E): number => {
if (typeof a === 'object' || typeof b === 'object') {
throw TypeError(
@ -31,9 +30,8 @@ export class MaxHeap<E = any> extends Heap<E> {
if (a < b) return 1;
if (a > b) return -1;
return 0;
}
}
) {
super(elements, options);
},
...options
});
}
}

View file

@ -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<E = any> extends Heap<E> {
constructor(elements: Iterable<E> = [], options?: HeapOptions<E>) {
export class MinHeap<E = any, R = any> extends Heap<E> {
constructor(elements: Iterable<E> | Iterable<R> = [], options?: HeapOptions<E, R>) {
super(elements, options);
}
}

View file

@ -8,7 +8,7 @@
import type { PriorityQueueOptions } from '../../types';
import { PriorityQueue } from './priority-queue';
export class MaxPriorityQueue<E = any> extends PriorityQueue<E> {
export class MaxPriorityQueue<E = any, R = any> extends PriorityQueue<E> {
/**
* The constructor initializes a PriorityQueue with optional elements and options, including a
* comparator function.
@ -16,21 +16,22 @@ export class MaxPriorityQueue<E = any> extends PriorityQueue<E> {
* 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<E> = [],
options: PriorityQueueOptions<E> = {
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<E> | Iterable<R> = [], options?: PriorityQueueOptions<E, R>) {
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
});
}
}

View file

@ -8,7 +8,7 @@
import type { PriorityQueueOptions } from '../../types';
import { PriorityQueue } from './priority-queue';
export class MinPriorityQueue<E = any> extends PriorityQueue<E> {
export class MinPriorityQueue<E = any, R = any> extends PriorityQueue<E> {
/**
* The constructor initializes a PriorityQueue with optional elements and options, including a
* comparator function.
@ -16,22 +16,11 @@ export class MinPriorityQueue<E = any> extends PriorityQueue<E> {
* 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<E> = [],
options: PriorityQueueOptions<E> = {
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<E> | Iterable<R> = [], options?: PriorityQueueOptions<E, R>) {
super(elements, options);
}
}

View file

@ -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<E = any> extends Heap<E> {
export class PriorityQueue<E = any, R = any> extends Heap<E, R> {
/**
* 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<E> = [], options?: PriorityQueueOptions<E>) {
constructor(elements: Iterable<E> | Iterable<R> = [], options?: PriorityQueueOptions<E, R>) {
super(elements, options);
}
}

View file

@ -6,6 +6,6 @@ export type BinaryTreeNodeNested<K, V> = BinaryTreeNode<K, V, BinaryTreeNode<K,
export type BinaryTreeNested<K, V, R, NODE extends BinaryTreeNode<K, V, NODE>> = BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, BinaryTree<K, V, R, NODE, any>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
export type BinaryTreeOptions<K, V, R> = {
iterationType?: IterationType
iterationType?: IterationType;
toEntryFn?: (rawElement: R) => BTNEntry<K, V>;
}

View file

@ -1,3 +1,6 @@
import { Comparator } from '../../common';
export type HeapOptions<T> = { comparator?: Comparator<T> };
export type HeapOptions<E, R> = {
comparator?: Comparator<E>;
toElementFn?: (rawElement: R) => E;
};

View file

@ -1,3 +1,3 @@
import { HeapOptions } from '../heap';
export type PriorityQueueOptions<T> = HeapOptions<T> & {};
export type PriorityQueueOptions<E, R> = HeapOptions<E, R> & {};

View file

@ -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<number, { rawItem: { id: number } }>(
[
{ 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', () => {

View file

@ -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<number, { rawItem: { id: number } }>(
[
{ 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]);
});
});