Merge pull request #4 from zrwusa/priorityq

[heap, priority queue] Heap improved. References #123: redesigned
This commit is contained in:
Madness 2023-10-15 22:20:50 +08:00 committed by GitHub
commit 5fcd9fe5e0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 286 additions and 675 deletions

View file

@ -1,8 +1,8 @@
/**
* data-structure-typed
*
* @author Tyler Zeng
* @copyright Copyright (c) 2022 Tyler Zeng <zrwusa@gmail.com>
* @author Kirk Qi
* @copyright Copyright (c) 2022 Kirk Qi <qilinaus@gmail.com>
* @license MIT License
*/
import {arrayRemove, uuidV4} from '../../utils';
@ -622,9 +622,7 @@ export abstract class AbstractGraph<
if (vertexOrKey instanceof AbstractVertex) distMap.set(vertexOrKey, Infinity);
}
const heap = new PriorityQueue<{key: number; val: V}>({
comparator: (a, b) => a.key - b.key
});
const heap = new PriorityQueue<{key: number; val: V}>((a, b) => a.key - b.key);
heap.add({key: 0, val: srcVertex});
distMap.set(srcVertex, 0);

View file

@ -1,212 +1,254 @@
/**
* data-structure-typed
*
* @author Kirk
* @copyright Copyright (c) 2022 Tyler Zeng <zrwusa@gmail.com>
* @author Kirk Qi
* @copyright Copyright (c) 2022 Kirk Qi <qilinaus@gmail.com>
* @license MIT License
*/
import {PriorityQueue} from '../priority-queue';
import type {HeapOptions} from '../../types';
export class HeapItem<V = any> {
/**
* The constructor function initializes an instance of a class with a priority and a value.
* @param {number} priority - The `priority` parameter is a number that represents the priority of the value. It is
* optional and has a default value of `NaN`.
* @param {V | null} [val=null] - The `val` parameter is of type `V | null`, which means it can accept a value of type
* `V` or `null`.
*/
constructor(priority: number = Number.MAX_SAFE_INTEGER, val: V | null = null) {
this._val = val;
this._priority = priority;
}
import type {CompareFunction} from '../../types';
private _priority: number;
export class Heap<T> {
private nodes: T[] = [];
private readonly comparator: CompareFunction<T>;
get priority(): number {
return this._priority;
}
set priority(value: number) {
this._priority = value;
}
private _val: V | null;
get val(): V | null {
return this._val;
}
set val(value: V | null) {
this._val = value;
}
}
export abstract class Heap<V = any> {
/**
* The function is a constructor for a class that initializes a priority callback function based on the
* options provided.
* @param [options] - An optional object that contains configuration options for the Heap.
*/
protected constructor(options?: HeapOptions<V>) {
if (options) {
const {priorityExtractor} = options;
if (priorityExtractor !== undefined && typeof priorityExtractor !== 'function') {
throw new Error('.constructor expects a valid priority function');
}
this._priorityExtractor = priorityExtractor || (el => +el);
} else {
this._priorityExtractor = el => +el;
}
}
protected abstract _pq: PriorityQueue<HeapItem<V>>;
get pq() {
return this._pq;
}
protected _priorityExtractor: (val: V) => number;
get priorityExtractor() {
return this._priorityExtractor;
constructor(comparator: CompareFunction<T>) {
this.comparator = comparator;
}
/**
* The function returns the size of a priority queue.
* @returns The size of the priority queue.
* Insert an element into the heap and maintain the heap properties.
* @param value - The element to be inserted.
*/
get size(): number {
return this._pq.size;
}
/**
* The function checks if a priority queue is empty.
* @returns {boolean} A boolean value indicating whether the size of the priority queue is less than 1.
*/
isEmpty(): boolean {
return this._pq.size < 1;
}
peek(isItem?: undefined): V | undefined;
peek(isItem: false): V | undefined;
peek(isItem: true): HeapItem<V> | null;
/**
* The `peek` function returns the top item in the priority queue without removing it.
* @returns The `peek()` method is returning either a `HeapItem<V>` object or `null`.Returns an val with the highest priority in the queue
*/
peek(isItem?: boolean): HeapItem<V> | null | V | undefined {
isItem = isItem ?? false;
const peeked = this._pq.peek();
return isItem ? peeked : peeked?.val;
}
peekLast(isItem?: undefined): V | undefined;
peekLast(isItem: false): V | undefined;
peekLast(isItem: true): HeapItem<V> | null;
/**
* The `peekLast` function returns the last item in the heap.
* @returns The method `peekLast()` returns either a `HeapItem<V>` object or `null`.Returns an val with the lowest priority in the queue
*/
peekLast(isItem?: boolean): HeapItem<V> | null | V | undefined {
isItem = isItem ?? false;
const leafItem = this._pq.leaf();
return isItem ? leafItem : leafItem?.val;
}
/**
* The `add` function adds an val to a priority queue with an optional priority value.
* @param {V} val - The `val` parameter represents the value that you want to add to the heap. It can be of any
* type.
* @param {number} [priority] - The `priority` parameter is an optional number that represents the priority of the
* val being added to the heap. If the `val` parameter is a number, then the `priority` parameter is set to
* the value of `val`. If the `val` parameter is not a number, then the
* @returns The `add` method returns the instance of the `Heap` class.
* @throws {Error} if priority is not a valid number
*/
add(priority: number, val?: V): Heap<V> {
val = val === undefined ? (priority as unknown as V) : val;
this._pq.add(new HeapItem<V>(priority, val));
add(value: T): Heap<T> {
this.nodes.push(value);
this.bubbleUp(this.nodes.length - 1);
return this;
}
poll(isItem?: undefined): V | undefined;
poll(isItem: false): V | undefined;
poll(isItem: true): HeapItem<V> | null;
/**
* The `poll` function returns the top item from a priority queue or null if the queue is empty.Removes and returns an val with the highest priority in the queue
* @returns either a HeapItem<V> object or null.
* Remove and return the top element (smallest or largest element) from the heap.
* @returns The top element or null if the heap is empty.
*/
poll(isItem?: boolean): HeapItem<V> | null | V | undefined {
isItem = isItem ?? false;
const top = this._pq.poll();
if (!top) {
poll(): T | null {
if (this.nodes.length === 0) {
return null;
}
if (this.nodes.length === 1) {
return this.nodes.pop() as T;
}
return isItem ? top : top.val;
const topValue = this.nodes[0];
this.nodes[0] = this.nodes.pop() as T;
this.sinkDown(0);
return topValue;
}
/**
* The function checks if a given node or value exists in the priority queue.
* @param {V | HeapItem<V>} node - The parameter `node` can be of type `V` or `HeapItem<V>`.
* @returns a boolean value.
* Float operation to maintain heap properties after adding an element.
* @param index - The index of the newly added element.
*/
has(node: V | HeapItem<V>): boolean {
if (node instanceof HeapItem) {
return this.pq.getNodes().includes(node);
} else {
return (
this.pq.getNodes().findIndex(item => {
return item.val === node;
}) !== -1
);
protected bubbleUp(index: number): void {
const element = this.nodes[index];
while (index > 0) {
const parentIndex = Math.floor((index - 1) / 2);
const parent = this.nodes[parentIndex];
if (this.comparator(element, parent) < 0) {
this.nodes[index] = parent;
this.nodes[parentIndex] = element;
index = parentIndex;
} else {
break;
}
}
}
toArray(isItem?: undefined): (V | undefined)[];
toArray(isItem: false): (V | undefined)[];
toArray(isItem: true): (HeapItem<V> | null)[];
/**
* The `toArray` function returns an array of `HeapItem<V>` objects.
* @returns An array of HeapItem<V> objects.Returns a sorted list of vals
* Sinking operation to maintain heap properties after removing the top element.
* @param index - The index from which to start sinking.
*/
toArray(isItem?: boolean): (HeapItem<V> | null | V | undefined)[] {
isItem = isItem ?? false;
const itemArray = this._pq.toArray();
protected sinkDown(index: number): void {
const leftChildIndex = 2 * index + 1;
const rightChildIndex = 2 * index + 2;
const length = this.nodes.length;
let targetIndex = index;
return isItem ? itemArray : itemArray.map(item => item.val);
}
if (leftChildIndex < length && this.comparator(this.nodes[leftChildIndex], this.nodes[targetIndex]) < 0) {
targetIndex = leftChildIndex;
}
if (rightChildIndex < length && this.comparator(this.nodes[rightChildIndex], this.nodes[targetIndex]) < 0) {
targetIndex = rightChildIndex;
}
sort(isItem?: undefined): (V | undefined)[];
sort(isItem: false): (V | undefined)[];
sort(isItem: true): (HeapItem<V> | null)[];
/**
* The function sorts the elements in the priority queue and returns either the sorted items or their values depending
* on the value of the isItem parameter.
* @param {boolean} [isItem] - The `isItem` parameter is a boolean flag that indicates whether the sorted result should
* be an array of `HeapItem<V>` objects or an array of the values (`V`) of those objects. If `isItem` is `true`, the
* sorted result will be an array of `HeapItem
* @returns an array of either `HeapItem<V>`, `null`, `V`, or `undefined` values.
*/
sort(isItem?: boolean): (HeapItem<V> | null | V | undefined)[] {
isItem = isItem ?? false;
const sorted = this._pq.sort();
return isItem ? sorted : sorted.map(item => item.val);
if (targetIndex !== index) {
const temp = this.nodes[index];
this.nodes[index] = this.nodes[targetIndex];
this.nodes[targetIndex] = temp;
this.sinkDown(targetIndex);
}
}
/**
* The clear function clears the priority queue.
* Fix the entire heap to maintain heap properties.
*/
clear(): void {
this._pq.clear();
protected fix() {
for (let i = Math.floor(this.size / 2); i >= 0; i--) this.sinkDown(i);
}
/**
* Peek at the top element of the heap without removing it.
* @returns The top element or null if the heap is empty.
*/
peek(): T | null {
if (this.nodes.length === 0) {
return null;
}
return this.nodes[0];
}
/**
* Get the size (number of elements) of the heap.
*/
get size(): number {
return this.nodes.length;
}
/**
* Get the last element in the heap, which is not necessarily a leaf node.
* @returns The last element or null if the heap is empty.
*/
leaf(): T | null {
return this.nodes[this.size - 1] ?? null;
}
/**
* Check if the heap is empty.
* @returns True if the heap is empty, otherwise false.
*/
isEmpty() {
return this.size === 0;
}
clear() {
this.nodes = [];
}
refill(nodes: T[]) {
this.nodes = nodes;
this.fix();
}
/**
* Use a comparison function to check whether a binary heap contains a specific element.
* @param value - the element to check.
* @returns Returns true if the specified element is contained; otherwise, returns false.
*/
has(value: T): boolean {
return this.nodes.includes(value);
}
/**
* Use a comparison function to find the index of an element in the heap.
* @param value - the element to find.
* @param index - the index currently being searched.
* @returns The index of the element, or -1 if not found.
*/
private findIndex(value: T, index: number): number {
if (index >= this.size) {
return -1;
}
const compareResult = this.comparator(value, this.nodes[index]);
if (compareResult === 0) {
return index; // Element found
} else if (compareResult < 0) {
// The element should be in the left subtree
return this.findIndex(value, 2 * index + 1);
} else {
// The element should be in the right subtree
return this.findIndex(value, 2 * index + 2);
}
}
/**
* Depth-first search (DFS) method, different traversal orders can be selected
* @param order - Traversal order parameter: 'in' (in-order), 'pre' (pre-order) or 'post' (post-order).
* @returns An array containing elements traversed in the specified order.
*/
dfs(order: 'in' | 'pre' | 'post'): T[] {
const result: T[] = [];
// Auxiliary recursive function, traverses the binary heap according to the traversal order
const dfsHelper = (index: number) => {
if (index < this.size) {
if (order === 'in') {
dfsHelper(2 * index + 1);
result.push(this.nodes[index]);
dfsHelper(2 * index + 2);
} else if (order === 'pre') {
result.push(this.nodes[index]);
dfsHelper(2 * index + 1);
dfsHelper(2 * index + 2);
} else if (order === 'post') {
dfsHelper(2 * index + 1);
dfsHelper(2 * index + 2);
result.push(this.nodes[index]);
}
}
};
dfsHelper(0); // Traverse starting from the root node
return result;
}
/**
* Convert the heap to an array.
* @returns An array containing the elements of the heap.
*/
toArray(): T[] {
return [...this.nodes];
}
getNodes(): T[] {
return this.nodes;
}
/**
* Clone the heap, creating a new heap with the same elements.
* @returns A new Heap instance containing the same elements.
*/
clone(): Heap<T> {
const clonedHeap = new Heap<T>(this.comparator);
clonedHeap.nodes = [...this.nodes];
return clonedHeap;
}
/**
* Sort the elements in the heap and return them as an array.
* @returns An array containing the elements sorted in ascending order.
*/
sort(): T[] {
const visitedNode: T[] = [];
const cloned = this.clone();
while (cloned.size !== 0) {
const top = cloned.poll();
if (top) visitedNode.push(top);
}
return visitedNode;
}
/**
* Static method that creates a binary heap from an array of nodes and a comparison function.
* @param nodes
* @param comparator - Comparison function.
* @returns A new Heap instance.
*/
static heapify<T>(nodes: T[], comparator: CompareFunction<T>): Heap<T> {
const binaryHeap = new Heap<T>(comparator);
binaryHeap.nodes = [...nodes];
binaryHeap.fix(); // Fix heap properties
return binaryHeap;
}
}

View file

@ -1,31 +1,24 @@
/**
* data-structure-typed
*
* @author Tyler Zeng
* @copyright Copyright (c) 2022 Tyler Zeng <zrwusa@gmail.com>
* @author Kirk Qi
* @copyright Copyright (c) 2022 Kirk Qi <qilinaus@gmail.com>
* @license MIT License
*/
import {Heap, HeapItem} from './heap';
import {PriorityQueue} from '../priority-queue';
import type {HeapOptions} from '../../types';
import {Heap} from './heap';
import type {CompareFunction} from '../../types';
/**
* @class MaxHeap
* @extends Heap
*/
export class MaxHeap<V = any> extends Heap<V> {
protected _pq: PriorityQueue<HeapItem<V>>;
/**
* The constructor initializes a PriorityQueue with a custom comparator function.
* @param [options] - The `options` parameter is an optional object that can be passed to the constructor. It is of
* type `HeapOptions<V>`, which is a generic type that represents the options for the heap.
*/
constructor(options?: HeapOptions<V>) {
super(options);
this._pq = new PriorityQueue<HeapItem<V>>({
comparator: (a, b) => b.priority - a.priority
});
export class MaxHeap<T = any> extends Heap<T> {
constructor(
comparator: CompareFunction<T> = (a: T, b: T) => {
if (!(typeof a === 'number' && typeof b === 'number')) {
throw new Error('The a, b params of compare function must be number');
} else {
return b - a;
}
}
) {
super(comparator);
}
}

View file

@ -1,32 +1,24 @@
/**
* data-structure-typed
*
* @author Tyler Zeng
* @copyright Copyright (c) 2022 Tyler Zeng <zrwusa@gmail.com>
* @author Kirk Qi
* @copyright Copyright (c) 2022 Kirk Qi <qilinaus@gmail.com>
* @license MIT License
*/
import {Heap, HeapItem} from './heap';
import {PriorityQueue} from '../priority-queue';
import type {HeapOptions} from '../../types';
import {Heap} from './heap';
import type {CompareFunction} from '../../types';
/**
* @class MinHeap
* @extends Heap
*/
export class MinHeap<V = any> extends Heap<V> {
protected _pq: PriorityQueue<HeapItem<V>>;
/**
* The constructor initializes a PriorityQueue with a comparator function that compares the priority of two HeapItem
* objects.
* @param [options] - The `options` parameter is an optional object that can be passed to the constructor. It is of
* type `HeapOptions<V>`, which is a generic type that represents the options for the heap.
*/
constructor(options?: HeapOptions<V>) {
super(options);
this._pq = new PriorityQueue<HeapItem<V>>({
comparator: (a, b) => a.priority - b.priority
});
export class MinHeap<T = any> extends Heap<T> {
constructor(
comparator: CompareFunction<T> = (a: T, b: T) => {
if (!(typeof a === 'number' && typeof b === 'number')) {
throw new Error('The a, b params of compare function must be number');
} else {
return a - b;
}
}
) {
super(comparator);
}
}

View file

@ -1,56 +1,23 @@
/**
* data-structure-typed
*
* @author Tyler Zeng
* @copyright Copyright (c) 2022 Tyler Zeng <zrwusa@gmail.com>
* @author Kirk Qi
* @copyright Copyright (c) 2022 Kirk Qi <qilinaus@gmail.com>
* @license MIT License
*/
import {PriorityQueue} from './priority-queue';
import type {PriorityQueueOptions, SpecifyOptional} from '../../types';
import type {CompareFunction} from '../../types';
export class MaxPriorityQueue<E = any> extends PriorityQueue<E> {
constructor(options?: Omit<PriorityQueueOptions<number>, 'comparator'>);
constructor(options: PriorityQueueOptions<E>);
/**
* The constructor initializes a priority queue with an optional comparator function.
* @param [options] - The `options` parameter is an optional object that can contain various properties to configure
* the priority queue.
*/
constructor(options?: SpecifyOptional<PriorityQueueOptions<E>, 'comparator'>) {
super({
...options,
comparator: options?.comparator
? options.comparator
: (a: E, b: E) => {
const aKey = a as unknown as number,
bKey = b as unknown as number;
return bKey - aKey;
}
});
}
static override heapify<E extends number>(options?: Omit<PriorityQueueOptions<E>, 'comparator'>): MaxPriorityQueue<E>;
static override heapify<E>(options: PriorityQueueOptions<E>): MaxPriorityQueue<E>;
/**
* The function `heapify` creates a max priority queue from the given options and returns it.
* @param options - The `options` parameter is an object that contains configuration options for creating a priority
* queue. It can have the following properties:
* @returns a MaxPriorityQueue object.
*/
static override heapify<E>(options: PriorityQueueOptions<E>): MaxPriorityQueue<E> {
const maxPQ = new MaxPriorityQueue<E>({
...options,
comparator: options?.comparator
? options.comparator
: (a: E, b: E) => {
const aKey = a as unknown as number,
bKey = b as unknown as number;
return bKey - aKey;
}
});
maxPQ._fix();
return maxPQ;
export class MaxPriorityQueue<T = any> extends PriorityQueue<T> {
constructor(
compare: CompareFunction<T> = (a: T, b: T) => {
if (!(typeof a === 'number' && typeof b === 'number')) {
throw new Error('The a, b params of compare function must be number');
} else {
return b - a;
}
}
) {
super(compare);
}
}

View file

@ -1,57 +1,23 @@
/**
* data-structure-typed
*
* @author Tyler Zeng
* @copyright Copyright (c) 2022 Tyler Zeng <zrwusa@gmail.com>
* @author Kirk Qi
* @copyright Copyright (c) 2022 Kirk Qi <qilinaus@gmail.com>
* @license MIT License
*/
import {PriorityQueue} from './priority-queue';
import type {PriorityQueueOptions, SpecifyOptional} from '../../types';
import type {CompareFunction} from '../../types';
export class MinPriorityQueue<E = any> extends PriorityQueue<E> {
constructor(options?: Omit<PriorityQueueOptions<number>, 'comparator'>);
constructor(options: PriorityQueueOptions<E>);
/**
* The constructor initializes a priority queue with an optional comparator function.
* @param [options] - The `options` parameter is an optional object that can contain various configuration options for
* the `PriorityQueue` constructor.
*/
constructor(options?: SpecifyOptional<PriorityQueueOptions<E>, 'comparator'>) {
super({
...options,
comparator: options?.comparator
? options.comparator
: (a: E, b: E) => {
const aKey = a as unknown as number,
bKey = b as unknown as number;
return aKey - bKey;
}
});
}
static override heapify<E extends number>(options?: Omit<PriorityQueueOptions<E>, 'comparator'>): MinPriorityQueue<E>;
static override heapify<E>(options: PriorityQueueOptions<E>): MinPriorityQueue<E>;
/**
* The function `heapify` creates a new MinPriorityQueue instance and sets the comparator function based on the options
* provided, and then fixes the heap structure of the queue.
* @param options - The `options` parameter is an object that contains configuration options for creating a priority
* queue. It can have the following properties:
* @returns a MinPriorityQueue object.
*/
static override heapify<E>(options: PriorityQueueOptions<E>): MinPriorityQueue<E> {
const minPQ = new MinPriorityQueue<E>({
...options,
comparator: options?.comparator
? options.comparator
: (a: E, b: E) => {
const aKey = a as unknown as number,
bKey = b as unknown as number;
return aKey - bKey;
}
});
minPQ._fix();
return minPQ;
export class MinPriorityQueue<T = any> extends PriorityQueue<T> {
constructor(
compare: CompareFunction<T> = (a: T, b: T) => {
if (!(typeof a === 'number' && typeof b === 'number')) {
throw new Error('The a, b params of compare function must be number');
} else {
return a - b;
}
}
) {
super(compare);
}
}

View file

@ -1,359 +1,16 @@
/**
* data-structure-typed
*
* @author Tyler Zeng
* @copyright Copyright (c) 2022 Tyler Zeng <zrwusa@gmail.com>
* @author Kirk Qi
* @copyright Copyright (c) 2022 Kirk Qi <qilinaus@gmail.com>
* @license MIT License
*/
import type {PriorityQueueComparator, PriorityQueueDFSOrderPattern, PriorityQueueOptions} from '../../types';
export class PriorityQueue<E = any> {
/**
* The constructor initializes a priority queue with the given options, including an array of nodes and a comparator
* function.
* @param options - The `options` parameter is an object that contains the following properties:
*/
constructor(options: PriorityQueueOptions<E>) {
const {nodes, comparator, isFix = true} = options;
this._comparator = comparator;
import {Heap} from '../heap';
import {CompareFunction} from '../../types';
if (nodes && Array.isArray(nodes) && nodes.length > 0) {
// TODO support distinct
this._nodes = [...nodes];
isFix && this._fix();
}
export class PriorityQueue<T> extends Heap<T> {
constructor(comparator: CompareFunction<T>) {
super(comparator);
}
protected _nodes: E[] = [];
get nodes(): E[] {
return this._nodes;
}
get size(): number {
return this.nodes.length;
}
/**
* The `heapify` function creates a new PriorityQueue instance and fixes the heap property.
* @param options - The "options" parameter is an object that contains the configuration options for the PriorityQueue.
* It can include properties such as "comparator" which specifies the comparison function used to order the elements in
* the priority queue, and "initialValues" which is an array of initial values to be added to the priority
* @returns a new instance of the PriorityQueue class after performing the heapify operation on it.
*/
static heapify<E>(options: PriorityQueueOptions<E>) {
const heap = new PriorityQueue(options);
heap._fix();
return heap;
}
/**
* The function checks if a priority queue is valid by creating a new priority queue with a fix option and then calling
* the isValid method.
* @param options - An object containing options for creating a priority queue. The options object should have the
* following properties:
* @returns the result of calling the `isValid()` method on a new instance of the `PriorityQueue` class.
*/
static isPriorityQueueified<E>(options: Omit<PriorityQueueOptions<E>, 'isFix'>) {
return new PriorityQueue({...options, isFix: false}).isValid();
}
/**
* Starting from TypeScript version 5.0 and onwards, the use of distinct access modifiers for Getters and Setters is not permitted. As an alternative, to ensure compatibility, it is necessary to adopt a Java-style approach for Setters (using the same name as the property) while utilizing separate method names for Getters.
*/
getNodes(): E[] {
return this._nodes;
}
/**
* The "add" function adds a node to the heap and ensures that the heap property is maintained.
* @param {E} node - The parameter "node" is of type E, which means it can be any data type. It represents the node
* that needs to be added to the heap.
*/
add(node: E) {
this.nodes.push(node);
this._heapifyUp(this.size - 1);
}
/**
* The "has" function checks if a given node is present in the list of nodes.
* @param {E} node - The parameter `node` is of type `E`, which means it can be any type. It represents the node that
* we want to check if it exists in the `nodes` array.
* @returns a boolean value indicating whether the given node is included in the array of nodes.
*/
has(node: E): boolean {
return this.nodes.includes(node);
}
/**
* The `peek` function returns the first element of the `nodes` array if it exists, otherwise it returns `null`.
* @returns The `peek()` function is returning the first element (`E`) of the `nodes` array if the `size` is not zero.
* Otherwise, it returns `null`.
*/
peek(): E | null {
return this.size ? this.nodes[0] : null;
}
/**
* The `poll` function removes and returns the top element from a heap data structure.
* @returns The `poll()` method returns a value of type `E` or `null`.
*/
poll(): E | null {
let res: E | null = null;
if (this.size > 1) {
this._swap(0, this.nodes.length - 1);
res = this.nodes.pop() ?? null;
this._heapifyDown(0);
} else if (this.size === 1) {
res = this.nodes.pop() ?? null;
}
return res;
}
/**
* The `leaf` function returns the last element in the `nodes` array or `null` if the array is empty.
* @returns The method `leaf()` is returning the last element (`E`) in the `nodes` array if it exists. If the array is
* empty or the last element is `null`, then it returns `null`.
*/
leaf(): E | null {
return this.nodes[this.size - 1] ?? null;
}
/**
* The function checks if the size of an object is equal to zero and returns a boolean value indicating whether the
* object is empty or not.
* @returns The method `isEmpty()` is returning a boolean value indicating whether the size of the object is equal to
* 0.
*/
isEmpty() {
return this.size === 0;
}
/**
* The clear function clears the nodes array.
*/
clear() {
this._setNodes([]);
}
/**
* The toArray function returns an array containing all the elements in the nodes property.
* @returns An array of type E, which is the elements of the nodes property.
*/
toArray(): E[] {
return [...this.nodes];
}
/**
* The `clone` function returns a new instance of the `PriorityQueue` class with the same nodes and comparator as the
* original instance.
* @returns The `clone()` method is returning a new instance of the `PriorityQueue` class with the same `nodes` and
* `comparator` properties as the original instance.
*/
clone(): PriorityQueue<E> {
return new PriorityQueue<E>({
nodes: this.nodes,
comparator: this._comparator
});
}
// --- start additional methods ---
/**
* The `isValid` function recursively checks if a binary tree satisfies a certain condition.
* @returns The function `isValid()` returns a boolean value.
*/
isValid(): boolean {
for (let i = 0; i < this.nodes.length; i++) {
const leftChildIndex = this._getLeft(i);
const rightChildIndex = this._getRight(i);
if (this._isValidIndex(leftChildIndex) && !this._compare(leftChildIndex, i)) {
return false;
}
if (this._isValidIndex(rightChildIndex) && !this._compare(rightChildIndex, i)) {
return false;
}
}
return true;
}
/**
* O(n log n), In scenarios with smaller data sizes, heap sort is generally expected to be slower than QuickSort or MergeSort.
*/
/**
* The function sorts the elements in a data structure and returns them in an array.
* Plan to support sorting of duplicate elements.
* @returns The `sort()` method is returning an array of type `E[]`.
*/
sort(): E[] {
const visitedNode: E[] = [];
while (this.size !== 0) {
const top = this.poll();
if (top) visitedNode.push(top);
}
return visitedNode;
}
/**
* The dfs function performs a depth-first search traversal on a binary tree and returns an array of visited nodes
* based on the specified traversal order.
* @param {PriorityQueueDFSOrderPattern} dfsMode - The dfsMode parameter is a string that specifies the order in which
* the nodes should be visited during the Depth-First Search (dfs) traversal. It can have one of the following values:
* @returns an array of type `(E | null)[]`.
*/
dfs(dfsMode: PriorityQueueDFSOrderPattern): (E | null)[] {
const visitedNode: (E | null)[] = [];
const traverse = (cur: number) => {
const leftChildIndex = this._getLeft(cur);
const rightChildIndex = this._getRight(cur);
switch (dfsMode) {
case 'in':
this._isValidIndex(leftChildIndex) && traverse(leftChildIndex);
visitedNode.push(this.nodes[cur] ?? null);
this._isValidIndex(rightChildIndex) && traverse(rightChildIndex);
break;
case 'pre':
visitedNode.push(this.nodes[cur] ?? null);
this._isValidIndex(leftChildIndex) && traverse(leftChildIndex);
this._isValidIndex(rightChildIndex) && traverse(rightChildIndex);
break;
case 'post':
this._isValidIndex(leftChildIndex) && traverse(leftChildIndex);
this._isValidIndex(rightChildIndex) && traverse(rightChildIndex);
visitedNode.push(this.nodes[cur] ?? null);
break;
}
};
this._isValidIndex(0) && traverse(0);
return visitedNode;
}
protected _setNodes(value: E[]) {
this._nodes = value;
}
protected readonly _comparator: PriorityQueueComparator<E> = (a: E, b: E) => {
const aKey = a as unknown as number,
bKey = b as unknown as number;
return aKey - bKey;
};
/**
* The function compares two numbers using a custom comparator function.
* @param {number} a - The parameter "a" is a number that represents the index of a node in an array.
* @param {number} b - The parameter "b" is a number.
* @returns the result of the comparison between the elements at indices `a` and `b` in the `nodes` array. The
* comparison is done using the `_comparator` function, and if the result is greater than 0, `true` is returned,
* indicating that the element at index `a` is greater than the element at index `b`.
*/
protected _compare(a: number, b: number) {
return this._comparator(this.nodes[a], this.nodes[b]) > 0;
}
/**
* The function swaps two elements in an array.
* @param {number} a - The parameter "a" is a number that represents the index of an element in an array.
* @param {number} b - The parameter "b" is a number.
*/
protected _swap(a: number, b: number) {
const temp = this.nodes[a];
this.nodes[a] = this.nodes[b];
this.nodes[b] = temp;
}
/**
* The function checks if a given index is valid within an array.
* @param {number} index - The parameter "index" is of type number and represents the index value that needs to be
* checked for validity.
* @returns A boolean value indicating whether the given index is valid or not.
*/
protected _isValidIndex(index: number): boolean {
return index > -1 && index < this.nodes.length;
}
/**
* The function returns the index of the parent node given the index of a child node in a binary tree.
* @param {number} child - The "child" parameter is a number representing the index of a child node in a binary tree.
* @returns the parent of the given child node.
*/
protected _getParent(child: number): number {
return Math.floor((child - 1) / 2);
}
/**
* The function returns the index of the left child node in a binary tree given the index of its parent node.
* @param {number} parent - The parameter "parent" is a number that represents the index of a node in a binary tree.
* @returns the left child of a given parent node in a binary tree.
*/
protected _getLeft(parent: number): number {
return 2 * parent + 1;
}
/**
* The function returns the index of the right child node in a binary tree given the index of its parent node.
* @param {number} parent - The parameter "parent" is a number that represents the index of a node in a binary tree.
* @returns the right child of a given parent node in a binary tree.
*/
protected _getRight(parent: number): number {
return 2 * parent + 2;
}
/**
* The function returns the index of the smallest child node of a given parent node.
* @param {number} parent - The parent parameter is a number that represents the index of the parent node in a binary
* tree.
* @returns the minimum value between the parent node and its left and right child nodes.
*/
protected _getComparedChild(parent: number) {
let min = parent;
const left = this._getLeft(parent),
right = this._getRight(parent);
if (left < this.size && this._compare(min, left)) {
min = left;
}
if (right < this.size && this._compare(min, right)) {
min = right;
}
return min;
}
/**
* The function `_heapifyUp` is used to maintain the heap property by moving an element up the heap until it is in the
* correct position.
* @param {number} start - The start parameter is the index of the element that needs to be moved up in the heap.
*/
protected _heapifyUp(start: number) {
while (start > 0 && this._compare(this._getParent(start), start)) {
const parent = this._getParent(start);
this._swap(start, parent);
start = parent;
}
}
/**
* The function performs a heapify operation by comparing and swapping elements in a binary heap.
* @param {number} start - The start parameter is the index of the element in the heap from where the heapifyDown
* operation should start.
*/
protected _heapifyDown(start: number) {
let min = this._getComparedChild(start);
while (this._compare(start, min)) {
this._swap(min, start);
start = min;
min = this._getComparedChild(start);
}
}
/**
* The _fix function performs a heapify operation on the elements of the heap starting from the middle and moving
* towards the root.
*/
protected _fix() {
for (let i = Math.floor(this.size / 2); i > -1; i--) this._heapifyDown(i);
}
// --- end additional methods ---
}

View file

@ -1,5 +1 @@
export type HeapOptions<T> = {
priorityExtractor?: (element: T) => number;
// TODO there is an idea that support chaining which is for conveniently using the data structure
// isChaining? : boolean
};
export type CompareFunction<T> = (a: T, b: T) => number;