X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=src%2Fpriority-queue.ts;h=ed4d63f5fa9cb03b71f1e91b472f84d1978d2573;hb=19421ff8b59772bfcf4921533c6d215f94327416;hp=8e9c1ae2db52fb4eba012fcc27be33d48b7e2885;hpb=2107bc577c9620f6140183691f8038e71304489b;p=poolifier.git diff --git a/src/priority-queue.ts b/src/priority-queue.ts index 8e9c1ae2..ed4d63f5 100644 --- a/src/priority-queue.ts +++ b/src/priority-queue.ts @@ -1,14 +1,20 @@ // Copyright Jerome Benoit. 2024. All Rights Reserved. +import { FixedPriorityQueue } from './fixed-priority-queue.js' + +/** + * Default bucket size. + */ +export const defaultBucketSize = 2048 + /** * Priority queue node. * * @typeParam T - Type of priority queue node data. * @internal */ -export interface PriorityQueueNode { - data: T - priority: number +export interface PriorityQueueNode extends FixedPriorityQueue { + next?: FixedPriorityQueue } /** @@ -18,30 +24,19 @@ export interface PriorityQueueNode { * @internal */ export class PriorityQueue { - private nodeArray!: Array> - /** Prioritized bucket size. */ + private head!: PriorityQueueNode + private tail!: PriorityQueueNode private readonly bucketSize: number - /** The size of the priority queue. */ - public size!: number - /** The maximum size of the priority queue. */ public maxSize!: number - /** - * The number of filled prioritized buckets. - */ - public get buckets (): number { - return this.bucketSize === Infinity - ? 1 - : Math.trunc(this.nodeArray.length / this.bucketSize) - } - /** * Constructs a priority queue. * - * @param bucketSize - Prioritized bucket size. @defaultValue Infinity + * @param bucketSize - Prioritized bucket size. @defaultValue defaultBucketSize + * @returns PriorityQueue. */ - public constructor (bucketSize = Infinity) { - if (bucketSize !== Infinity && !Number.isSafeInteger(bucketSize)) { + public constructor (bucketSize: number = defaultBucketSize) { + if (!Number.isSafeInteger(bucketSize)) { throw new TypeError('bucketSize must be an integer') } if (bucketSize < 1) { @@ -51,6 +46,26 @@ export class PriorityQueue { this.clear() } + /** + * The size of the priority queue. + */ + public get size (): number { + let node: PriorityQueueNode | undefined = this.tail + let size = 0 + while (node != null) { + size += node.size + node = node.next + } + return size + } + + /** + * The number of filled prioritized buckets. + */ + public get buckets (): number { + return Math.trunc(this.size / this.bucketSize) + } + /** * Enqueue data into the priority queue. * @@ -59,67 +74,63 @@ export class PriorityQueue { * @returns The new size of the priority queue. */ public enqueue (data: T, priority?: number): number { - priority = priority ?? 0 - const startIndex = - this.bucketSize === Infinity ? 0 : this.buckets * this.bucketSize - let inserted = false - for (let index = startIndex; index < this.nodeArray.length; index++) { - if (this.nodeArray[index].priority > priority) { - this.nodeArray.splice(index, 0, { data, priority }) - inserted = true - break - } + if (this.head.full()) { + this.head = this.head.next = new FixedPriorityQueue(this.bucketSize) } - if (!inserted) { - this.nodeArray.push({ data, priority }) + this.head.enqueue(data, priority) + const size = this.size + if (size > this.maxSize) { + this.maxSize = size } - return this.incrementSize() + return size } /** * Dequeue data from the priority queue. * - * @param bucket - The prioritized bucket to dequeue from. @defaultValue 0 + * @param bucket - The prioritized bucket to dequeue from. * @returns The dequeued data or `undefined` if the priority queue is empty. */ - public dequeue (bucket = 0): T | undefined { - if (this.bucketSize !== Infinity && bucket > 0) { - while (bucket > 0) { - const index = bucket * this.bucketSize - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - if (this.nodeArray[index] != null) { - --this.size - return this.nodeArray.splice(index, 1)[0].data + public dequeue (bucket?: number): T | undefined { + let tail: PriorityQueueNode | undefined = this.tail + if (bucket != null) { + let currentBucket = 0 + while (tail != null) { + ++currentBucket + if (currentBucket === bucket) { + break } - --bucket + tail = tail.next } } - this.decrementSize() - return this.nodeArray.shift()?.data - } - - /** - * Peeks at the first data. - * @returns The first data or `undefined` if the priority queue is empty. - */ - public peekFirst (): T | undefined { - return this.nodeArray[0]?.data - } - - /** - * Peeks at the last data. - * @returns The last data or `undefined` if the priority queue is empty. - */ - public peekLast (): T | undefined { - return this.nodeArray[this.nodeArray.length - 1]?.data + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const data = tail!.dequeue() + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + if (tail!.empty() && tail!.next != null) { + if (tail === this.tail) { + this.tail = tail.next + } else { + let node: PriorityQueueNode | undefined = this.tail + while (node != null) { + if (node.next === tail) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + node.next = tail!.next + break + } + node = node.next + } + } + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + delete tail!.next + } + return data } /** * Clears the priority queue. */ public clear (): void { - this.nodeArray = [] - this.size = 0 + this.head = this.tail = new FixedPriorityQueue(this.bucketSize) this.maxSize = 0 } @@ -129,18 +140,23 @@ export class PriorityQueue { * @returns An iterator for the priority queue. * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols */ - [Symbol.iterator] (): Iterator { + public [Symbol.iterator] (): Iterator { let i = 0 + let node = this.tail return { next: () => { - if (i >= this.nodeArray.length) { + if (node.empty() || (i >= node.capacity && node.next == null)) { return { value: undefined, done: true } } - const value = this.nodeArray[i].data - i++ + const value = node.dequeue() as T + ++i + if (i === node.capacity && node.next != null) { + node = node.next + i = 0 + } return { value, done: false @@ -148,29 +164,4 @@ export class PriorityQueue { } } } - - /** - * Increments the size of the priority queue. - * - * @returns The new size of the priority queue. - */ - private incrementSize (): number { - ++this.size - if (this.size > this.maxSize) { - this.maxSize = this.size - } - return this.size - } - - /** - * Decrements the size of the priority queue. - * - * @returns The new size of the priority queue. - */ - private decrementSize (): number { - if (this.size > 0) { - --this.size - } - return this.size - } }