feat: To support conversion between data structures, Heap, PriorityQueue, Queue, Deque, SinglyLinkedList, DoublyLinkedList, and Stack have all added an 'elements' parameter for initializing the elements of the data structure.

This commit is contained in:
Revone 2023-11-23 18:40:30 +08:00
parent c8210d9b0c
commit 8f9eb82bfd
22 changed files with 210 additions and 94 deletions

View file

@ -691,7 +691,7 @@ export abstract class AbstractGraph<
if (vertexOrKey instanceof AbstractVertex) distMap.set(vertexOrKey, Infinity);
}
const heap = new PriorityQueue<{ key: number; value: VO }>({ comparator: (a, b) => a.key - b.key });
const heap = new PriorityQueue<{ key: number; value: VO }>([], { comparator: (a, b) => a.key - b.key });
heap.add({ key: 0, value: srcVertex });
distMap.set(srcVertex, 0);

View file

@ -6,13 +6,32 @@
*/
import type { Comparator, DFSOrderPattern } from '../../types';
import { HeapOptions } from "../../types";
export class Heap<E = any> {
constructor(options: { comparator: Comparator<E>; elements?: E[] }) {
this._comparator = options.comparator;
if (options.elements && options.elements.length > 0) {
this._elements = options.elements;
this.fix();
options: HeapOptions<E>;
constructor(elements?: Iterable<E>, options?: HeapOptions<E>) {
const defaultComparator = (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;
}
}
if (options) {
this.options = options
} else {
this.options = {
comparator: defaultComparator
}
}
if (elements) {
for (const el of elements) {
this.push(el);
}
// this.fix();
}
}
@ -22,12 +41,6 @@ export class Heap<E = any> {
return this._elements;
}
protected _comparator: Comparator<E>;
get comparator(): Comparator<E> {
return this._comparator;
}
/**
* Get the size (number of elements) of the heap.
*/
@ -46,10 +59,11 @@ export class Heap<E = any> {
/**
* Static method that creates a binary heap from an array of elements and a comparison function.
* @returns A new Heap instance.
* @param elements
* @param options
*/
static heapify<E>(options: { elements: E[]; comparator: Comparator<E> }): Heap<E> {
return new Heap<E>(options);
static heapify<E>(elements: Iterable<E>, options: { comparator: Comparator<E> }): Heap<E> {
return new Heap<E>(elements, options);
}
/**
@ -283,7 +297,7 @@ export class Heap<E = any> {
* @returns A new Heap instance containing the same elements.
*/
clone(): Heap<E> {
const clonedHeap = new Heap<E>({ comparator: this.comparator });
const clonedHeap = new Heap<E>([], this.options);
clonedHeap._elements = [...this.elements];
return clonedHeap;
}
@ -340,7 +354,7 @@ export class Heap<E = any> {
}
filter(predicate: (element: E, index: number, heap: Heap<E>) => boolean): Heap<E> {
const filteredHeap: Heap<E> = new Heap<E>({ comparator: this.comparator });
const filteredHeap: Heap<E> = new Heap<E>([], this.options);
let index = 0;
for (const el of this) {
if (predicate(el, index, this)) {
@ -353,7 +367,7 @@ export class Heap<E = any> {
map<T>(callback: (element: E, index: number, heap: Heap<E>) => T, comparator: Comparator<T>): Heap<T> {
const mappedHeap: Heap<T> = new Heap<T>({ comparator: comparator });
const mappedHeap: Heap<T> = new Heap<T>([], { comparator: comparator });
let index = 0;
for (const el of this) {
mappedHeap.add(callback(el, index, this));
@ -380,6 +394,15 @@ export class Heap<E = any> {
* Space Complexity: O(1)
*/
print(): void {
console.log([...this]);
}
/**
* Time Complexity: O(n)
* Space Complexity: O(1)
*/
/**
* Time Complexity: O(log n)
* Space Complexity: O(1)
@ -392,18 +415,13 @@ export class Heap<E = any> {
while (index > 0) {
const parent = (index - 1) >> 1;
const parentItem = this.elements[parent];
if (this._comparator(parentItem, element) <= 0) break;
if (this.options.comparator(parentItem, element) <= 0) break;
this.elements[index] = parentItem;
index = parent;
}
this.elements[index] = element;
}
/**
* Time Complexity: O(n)
* Space Complexity: O(1)
*/
/**
* Time Complexity: O(log n)
* Space Complexity: O(1)
@ -420,12 +438,12 @@ export class Heap<E = any> {
let minItem = this.elements[left];
if (
right < this.elements.length &&
this._comparator(minItem, this.elements[right]) > 0
this.options.comparator(minItem, this.elements[right]) > 0
) {
left = right;
minItem = this.elements[right];
}
if (this._comparator(minItem, element) >= 0) break;
if (this.options.comparator(minItem, element) >= 0) break;
this.elements[index] = minItem;
index = left;
}
@ -452,7 +470,7 @@ export class FibonacciHeapNode<E> {
export class FibonacciHeap<E> {
constructor(comparator?: Comparator<E>) {
this.clear();
this._comparator = comparator || this.defaultComparator;
this._comparator = comparator || this._defaultComparator;
if (typeof this.comparator !== 'function') {
throw new Error('FibonacciHeap constructor: given comparator should be a function.');
@ -653,7 +671,7 @@ export class FibonacciHeap<E> {
this._root = undefined;
} else {
this._min = z.right;
this.consolidate();
this._consolidate();
}
this._size--;
@ -705,27 +723,27 @@ export class FibonacciHeap<E> {
heapToMerge.clear();
}
/**
* Create a new node.
* @param element
* @protected
*/
createNode(element: E): FibonacciHeapNode<E> {
return new FibonacciHeapNode<E>(element);
}
/**
* Default comparator function used by the heap.
* @param {E} a
* @param {E} b
* @protected
*/
protected defaultComparator(a: E, b: E): number {
protected _defaultComparator(a: E, b: E): number {
if (a < b) return -1;
if (a > b) return 1;
return 0;
}
/**
* Create a new node.
* @param element
* @protected
*/
protected createNode(element: E): FibonacciHeapNode<E> {
return new FibonacciHeapNode<E>(element);
}
/**
* Time Complexity: O(1)
* Space Complexity: O(1)
@ -782,7 +800,7 @@ export class FibonacciHeap<E> {
* @param x
* @protected
*/
protected link(y: FibonacciHeapNode<E>, x: FibonacciHeapNode<E>): void {
protected _link(y: FibonacciHeapNode<E>, x: FibonacciHeapNode<E>): void {
this.removeFromRoot(y);
y.left = y;
y.right = y;
@ -803,7 +821,7 @@ export class FibonacciHeap<E> {
* Remove and return the top element (smallest or largest element) from the heap.
* @protected
*/
protected consolidate(): void {
protected _consolidate(): void {
const A: (FibonacciHeapNode<E> | undefined)[] = new Array(this.size);
const elements = this.consumeLinkedList(this.root);
let x: FibonacciHeapNode<E> | undefined,
@ -824,7 +842,7 @@ export class FibonacciHeap<E> {
y = t;
}
this.link(y, x);
this._link(y, x);
A[d] = undefined;
d++;
}

View file

@ -7,11 +7,12 @@
*/
import { Heap } from './heap';
import type { Comparator } from '../../types';
import type { HeapOptions } from '../../types';
export class MaxHeap<E = any> extends Heap<E> {
constructor(
options: { comparator: Comparator<E>; nodes?: E[] } = {
elements?: Iterable<E>,
options: HeapOptions<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');
@ -19,8 +20,7 @@ export class MaxHeap<E = any> extends Heap<E> {
return b - a;
}
}
}
) {
super(options);
}) {
super(elements, options);
}
}

View file

@ -7,11 +7,12 @@
*/
import { Heap } from './heap';
import type { Comparator } from '../../types';
import type { HeapOptions } from '../../types';
export class MinHeap<E = any> extends Heap<E> {
constructor(
options: { comparator: Comparator<E>; nodes?: E[] } = {
elements?: Iterable<E>,
options: HeapOptions<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');
@ -19,8 +20,7 @@ export class MinHeap<E = any> extends Heap<E> {
return a - b;
}
}
}
) {
super(options);
}) {
super(elements, options);
}
}

View file

@ -26,10 +26,15 @@ export class DoublyLinkedList<E = any> {
/**
* The constructor initializes the linked list with an empty head, tail, and length.
*/
constructor() {
constructor(elements?: Iterable<E>) {
this._head = null;
this._tail = null;
this._length = 0;
if (elements) {
for (const el of elements) {
this.push(el);
}
}
}
protected _head: DoublyLinkedListNode<E> | null;
@ -834,4 +839,8 @@ export class DoublyLinkedList<E = any> {
return accumulator;
}
print(): void {
console.log([...this]);
}
}

View file

@ -24,10 +24,14 @@ export class SinglyLinkedList<E = any> {
/**
* The constructor initializes the linked list with an empty head, tail, and length.
*/
constructor() {
constructor(elements?: Iterable<E>) {
this._head = null;
this._tail = null;
this._length = 0;
if (elements) {
for (const el of elements)
this.push(el);
}
}
protected _head: SinglyLinkedListNode<E> | null;
@ -781,4 +785,8 @@ export class SinglyLinkedList<E = any> {
return accumulator;
}
print(): void {
console.log([...this]);
}
}

View file

@ -6,11 +6,12 @@
* @license MIT License
*/
import { PriorityQueue } from './priority-queue';
import type { Comparator } from '../../types';
import type { PriorityQueueOptions } from '../../types';
export class MaxPriorityQueue<E = any> extends PriorityQueue<E> {
constructor(
options: { comparator: Comparator<E>; nodes?: E[] } = {
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');
@ -20,6 +21,6 @@ export class MaxPriorityQueue<E = any> extends PriorityQueue<E> {
}
}
) {
super(options);
super(elements, options);
}
}

View file

@ -6,20 +6,20 @@
* @license MIT License
*/
import { PriorityQueue } from './priority-queue';
import type { Comparator } from '../../types';
import type { PriorityQueueOptions } from '../../types';
export class MinPriorityQueue<E = any> extends PriorityQueue<E> {
constructor(
options: { comparator: Comparator<E>; nodes?: 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>,
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;
}
}
}
) {
super(options);
super(elements, options);
}
}

View file

@ -7,10 +7,10 @@
*/
import { Heap } from '../heap';
import { Comparator } from '../../types';
import { PriorityQueueOptions } from '../../types';
export class PriorityQueue<E = any> extends Heap<E> {
constructor(options: { comparator: Comparator<E>; nodes?: E[] }) {
super(options);
constructor(elements?: Iterable<E>, options?: PriorityQueueOptions<E>) {
super(elements, options);
}
}

View file

@ -819,6 +819,10 @@ export class Deque<E> {
return accumulator;
}
print(): void {
console.log([...this])
}
/**
* Time Complexity: O(n)
* Space Complexity: O(n)

View file

@ -300,6 +300,10 @@ export class Queue<E = any> {
return new Queue(this.nodes.slice(this.offset));
}
print(): void {
console.log([...this]);
}
* [Symbol.iterator]() {
for (const item of this.nodes) {
yield item;

View file

@ -10,8 +10,13 @@ export class Stack<E = any> {
* of elements of type `E`. It is used to initialize the `_elements` property of the class. If the `elements` parameter
* is provided and is an array, it is assigned to the `_elements
*/
constructor(elements?: E[]) {
this._elements = Array.isArray(elements) ? elements : [];
constructor(elements?: Iterable<E>) {
this._elements = [];
if (elements) {
for (const el of elements) {
this.push(el);
}
}
}
protected _elements: E[];
@ -153,7 +158,7 @@ export class Stack<E = any> {
* @returns An iterator object.
*/
* [Symbol.iterator]() {
for (let i = this.elements.length - 1; i >= 0; i--) {
for (let i = 0; i < this.elements.length; i++) {
yield this.elements[i];
}
}
@ -203,4 +208,8 @@ export class Stack<E = any> {
}
return accumulator;
}
print(): void {
console.log([...this]);
}
}

View file

@ -1 +1,3 @@
export {};
import { Comparator } from "../../common";
export type HeapOptions<T> = { comparator: Comparator<T> }

View file

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

View file

@ -23,7 +23,7 @@ describe('Heap Operation Test', () => {
});
it('should object heap work well', function () {
const minHeap = new MinHeap<{ a: string; key: number }>({ comparator: (a, b) => a.key - b.key });
const minHeap = new MinHeap<{ a: string; key: number }>([], { comparator: (a, b) => a.key - b.key });
minHeap.add({ key: 1, a: 'a1' });
minHeap.add({ key: 6, a: 'a6' });
minHeap.add({ key: 2, a: 'a2' });
@ -43,7 +43,7 @@ describe('Heap Operation Test', () => {
i++;
}
const maxHeap = new MaxHeap<{ key: number; a: string }>({ comparator: (a, b) => b.key - a.key });
const maxHeap = new MaxHeap<{ key: number; a: string }>([], { comparator: (a, b) => b.key - a.key });
maxHeap.add({ key: 1, a: 'a1' });
maxHeap.add({ key: 6, a: 'a6' });
maxHeap.add({ key: 5, a: 'a5' });

View file

@ -5,7 +5,7 @@ describe('MaxHeap', () => {
let maxHeap: MaxHeap<number>;
beforeEach(() => {
maxHeap = new MaxHeap({ comparator: numberComparator });
maxHeap = new MaxHeap([], { comparator: numberComparator });
});
it('add and poll elements in descending order', () => {

View file

@ -5,7 +5,7 @@ describe('MinHeap', () => {
let minHeap: MinHeap<number>;
beforeEach(() => {
minHeap = new MinHeap({ comparator: numberComparator });
minHeap = new MinHeap([], { comparator: numberComparator });
});
it('add and poll elements in ascending order', () => {

View file

@ -16,7 +16,7 @@ describe('MaxPriorityQueue Operation Test', () => {
});
it('should add elements and maintain heap property in a object MaxPriorityQueue', () => {
const priorityQueue = new MaxPriorityQueue<{ keyA: number }>({ comparator: (a, b) => b.keyA - a.keyA });
const priorityQueue = new MaxPriorityQueue<{ keyA: number }>([], { comparator: (a, b) => b.keyA - a.keyA });
priorityQueue.refill([{ keyA: 5 }, { keyA: 3 }, { keyA: 1 }]);
priorityQueue.add({ keyA: 7 });
@ -52,7 +52,7 @@ describe('MaxPriorityQueue Operation Test', () => {
it('should correctly heapify an array', () => {
const array = [5, 3, 7, 1];
const heap = MaxPriorityQueue.heapify<number>({ elements: array, comparator: (a, b) => b - a });
const heap = MaxPriorityQueue.heapify<number>(array, { comparator: (a, b) => b - a });
heap.refill(array);
expect(heap.poll()).toBe(7);
@ -63,7 +63,8 @@ describe('MaxPriorityQueue Operation Test', () => {
it('should correctly heapify an object array', () => {
const elements = [{ keyA: 5 }, { keyA: 3 }, { keyA: 7 }, { keyA: 1 }];
const maxPQ = MaxPriorityQueue.heapify<{ keyA: number }>({ elements, comparator: (a, b) => b.keyA - a.keyA });
debugger
const maxPQ = MaxPriorityQueue.heapify<{ keyA: number }>(elements, { comparator: (a, b) => b.keyA - a.keyA });
expect(maxPQ.poll()?.keyA).toBe(7);
expect(maxPQ.poll()?.keyA).toBe(5);

View file

@ -5,7 +5,7 @@ import { isDebugTest } from '../../../config';
const isDebug = isDebugTest;
describe('PriorityQueue Operation Test', () => {
it('should PriorityQueue poll, pee, heapify, toArray work well', function () {
const minPQ = new PriorityQueue<number>({ comparator: (a, b) => a - b });
const minPQ = new PriorityQueue<number>([], { comparator: (a, b) => a - b });
minPQ.refill([5, 2, 3, 4, 6, 1]);
expect(minPQ.toArray()).toEqual([1, 2, 3, 4, 6, 5]);
minPQ.poll();
@ -14,15 +14,14 @@ describe('PriorityQueue Operation Test', () => {
expect(minPQ.toArray()).toEqual([4, 5, 6]);
expect(minPQ.peek()).toBe(4);
expect(
PriorityQueue.heapify({
elements: [3, 2, 1, 5, 6, 7, 8, 9, 10],
PriorityQueue.heapify([3, 2, 1, 5, 6, 7, 8, 9, 10], {
comparator: (a, b) => a - b
}).toArray()
).toEqual([1, 2, 3, 5, 6, 7, 8, 9, 10]);
).toEqual([1, 3, 2, 5, 6, 7, 8, 9, 10]);
});
it('should Max PriorityQueue poll, peek, heapify, toArray work well', function () {
const maxPriorityQueue = new PriorityQueue<number>({ comparator: (a, b) => b - a });
const maxPriorityQueue = new PriorityQueue<number>([], { comparator: (a, b) => b - a });
maxPriorityQueue.refill([5, 2, 3, 4, 6, 1]);
expect(maxPriorityQueue.toArray()).toEqual([6, 5, 3, 4, 2, 1]);
maxPriorityQueue.poll();
@ -31,15 +30,15 @@ describe('PriorityQueue Operation Test', () => {
expect(maxPriorityQueue.toArray()).toEqual([3, 2, 1]);
expect(maxPriorityQueue.peek()).toBe(3);
expect(
PriorityQueue.heapify({
elements: [3, 2, 1, 5, 6, 7, 8, 9, 10],
PriorityQueue.heapify([3, 2, 1, 5, 6, 7, 8, 9, 10], {
comparator: (a, b) => a - b
}).toArray()
).toEqual([1, 2, 3, 5, 6, 7, 8, 9, 10]);
).toEqual([1, 3, 2, 5, 6, 7, 8, 9, 10]);
});
it('should PriorityQueue clone, sort, getNodes, dfs work well', function () {
const minPQ1 = new PriorityQueue<number>({ comparator: (a, b) => a - b });
const minPQ1 = new PriorityQueue<number>([], { comparator: (a, b) => a - b });
minPQ1.refill([2, 5, 8, 3, 1, 6, 7, 4]);
const clonedPriorityQueue = minPQ1.clone();
expect(clonedPriorityQueue.elements).toEqual(minPQ1.elements);
@ -52,7 +51,7 @@ describe('PriorityQueue Operation Test', () => {
describe('Priority Queue Performance Test', () => {
it('should numeric heap work well', function () {
const pq = new PriorityQueue({ comparator: (a, b) => b - a });
const pq = new PriorityQueue<number>([], { comparator: (a, b) => b - a });
const tS = performance.now();

View file

@ -83,7 +83,7 @@ describe('Stack iterative methods', () => {
result.push(element);
}
expect(result).toEqual([3, 2, 1]); // iteration should start from the top of the stack
expect(result).toEqual([1, 2, 3]); // iteration should start from the top of the stack
});
test('should apply forEach to the stack', () => {
@ -92,7 +92,7 @@ describe('Stack iterative methods', () => {
result.push(element);
});
expect(result).toEqual([3, 2, 1]);
expect(result).toEqual([1, 2, 3]);
});
test('should filter elements in the stack', () => {

View file

@ -0,0 +1,59 @@
import {
Deque,
DoublyLinkedList,
MaxHeap,
MaxPriorityQueue,
MinHeap,
MinPriorityQueue,
Queue,
SinglyLinkedList,
Stack
} from '../../src';
const orgArr: number[] = [6, 1, 2, 7, 5, 3, 4, 9, 8];
describe('conversions', () => {
it('Array to Queue', () => {
const q = new Queue<number>(orgArr);
q.print();
})
it('Array to Deque', () => {
const dq = new Deque<number>(orgArr);
dq.print();
})
it('Array to SinglyLinkedList', () => {
const sl = new SinglyLinkedList<number>(orgArr);
sl.print();
})
it('Array to DoublyLinkedList', () => {
const dl = new DoublyLinkedList<number>(orgArr);
dl.print();
})
it('Array to Stack', () => {
const stack = new Stack<number>(orgArr);
stack.print();
})
it('Array to MinHeap', () => {
const minHeap = new MinHeap<number>(orgArr);
minHeap.print();
})
it('Array to MaxHeap', () => {
const maxHeap = new MaxHeap<number>(orgArr);
maxHeap.print();
})
it('Array to MinPriorityQueue', () => {
const minPQ = new MinPriorityQueue<number>(orgArr);
minPQ.print();
})
it('Array to MaxPriorityQueue', () => {
const maxPQ = new MaxPriorityQueue<number>(orgArr);
maxPQ.print();
})
})