- Deleted event-utils.ts which contained deprecated Port80Handler and its subscribers. - Updated index.ts to remove the export of event-utils. - Refactored ConnectionManager to extend LifecycleComponent for better resource management. - Added BinaryHeap implementation for efficient priority queue operations. - Introduced EnhancedConnectionPool for managing pooled connections with lifecycle management. - Implemented LifecycleComponent to manage timers and event listeners automatically. - Added comprehensive tests for BinaryHeap and LifecycleComponent to ensure functionality.
225 lines
5.2 KiB
TypeScript
225 lines
5.2 KiB
TypeScript
/**
|
|
* A binary heap implementation for efficient priority queue operations
|
|
* Supports O(log n) insert and extract operations
|
|
*/
|
|
export class BinaryHeap<T> {
|
|
private heap: T[] = [];
|
|
private keyMap?: Map<string, number>; // For efficient key-based lookups
|
|
|
|
constructor(
|
|
private compareFn: (a: T, b: T) => number,
|
|
private extractKey?: (item: T) => string
|
|
) {
|
|
if (extractKey) {
|
|
this.keyMap = new Map();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the current size of the heap
|
|
*/
|
|
public get size(): number {
|
|
return this.heap.length;
|
|
}
|
|
|
|
/**
|
|
* Check if the heap is empty
|
|
*/
|
|
public isEmpty(): boolean {
|
|
return this.heap.length === 0;
|
|
}
|
|
|
|
/**
|
|
* Peek at the top element without removing it
|
|
*/
|
|
public peek(): T | undefined {
|
|
return this.heap[0];
|
|
}
|
|
|
|
/**
|
|
* Insert a new item into the heap
|
|
* O(log n) time complexity
|
|
*/
|
|
public insert(item: T): void {
|
|
const index = this.heap.length;
|
|
this.heap.push(item);
|
|
|
|
if (this.keyMap && this.extractKey) {
|
|
const key = this.extractKey(item);
|
|
this.keyMap.set(key, index);
|
|
}
|
|
|
|
this.bubbleUp(index);
|
|
}
|
|
|
|
/**
|
|
* Extract the top element from the heap
|
|
* O(log n) time complexity
|
|
*/
|
|
public extract(): T | undefined {
|
|
if (this.heap.length === 0) return undefined;
|
|
if (this.heap.length === 1) {
|
|
const item = this.heap.pop()!;
|
|
if (this.keyMap && this.extractKey) {
|
|
this.keyMap.delete(this.extractKey(item));
|
|
}
|
|
return item;
|
|
}
|
|
|
|
const result = this.heap[0];
|
|
const lastItem = this.heap.pop()!;
|
|
this.heap[0] = lastItem;
|
|
|
|
if (this.keyMap && this.extractKey) {
|
|
this.keyMap.delete(this.extractKey(result));
|
|
this.keyMap.set(this.extractKey(lastItem), 0);
|
|
}
|
|
|
|
this.bubbleDown(0);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Extract an element that matches the predicate
|
|
* O(n) time complexity for search, O(log n) for extraction
|
|
*/
|
|
public extractIf(predicate: (item: T) => boolean): T | undefined {
|
|
const index = this.heap.findIndex(predicate);
|
|
if (index === -1) return undefined;
|
|
|
|
return this.extractAt(index);
|
|
}
|
|
|
|
/**
|
|
* Extract an element by its key (if extractKey was provided)
|
|
* O(log n) time complexity
|
|
*/
|
|
public extractByKey(key: string): T | undefined {
|
|
if (!this.keyMap || !this.extractKey) {
|
|
throw new Error('extractKey function must be provided to use key-based extraction');
|
|
}
|
|
|
|
const index = this.keyMap.get(key);
|
|
if (index === undefined) return undefined;
|
|
|
|
return this.extractAt(index);
|
|
}
|
|
|
|
/**
|
|
* Check if a key exists in the heap
|
|
* O(1) time complexity
|
|
*/
|
|
public hasKey(key: string): boolean {
|
|
if (!this.keyMap) return false;
|
|
return this.keyMap.has(key);
|
|
}
|
|
|
|
/**
|
|
* Get all elements as an array (does not modify heap)
|
|
* O(n) time complexity
|
|
*/
|
|
public toArray(): T[] {
|
|
return [...this.heap];
|
|
}
|
|
|
|
/**
|
|
* Clear the heap
|
|
*/
|
|
public clear(): void {
|
|
this.heap = [];
|
|
if (this.keyMap) {
|
|
this.keyMap.clear();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extract element at specific index
|
|
*/
|
|
private extractAt(index: number): T {
|
|
const item = this.heap[index];
|
|
|
|
if (this.keyMap && this.extractKey) {
|
|
this.keyMap.delete(this.extractKey(item));
|
|
}
|
|
|
|
if (index === this.heap.length - 1) {
|
|
this.heap.pop();
|
|
return item;
|
|
}
|
|
|
|
const lastItem = this.heap.pop()!;
|
|
this.heap[index] = lastItem;
|
|
|
|
if (this.keyMap && this.extractKey) {
|
|
this.keyMap.set(this.extractKey(lastItem), index);
|
|
}
|
|
|
|
// Try bubbling up first
|
|
const parentIndex = Math.floor((index - 1) / 2);
|
|
if (parentIndex >= 0 && this.compareFn(this.heap[index], this.heap[parentIndex]) < 0) {
|
|
this.bubbleUp(index);
|
|
} else {
|
|
this.bubbleDown(index);
|
|
}
|
|
|
|
return item;
|
|
}
|
|
|
|
/**
|
|
* Bubble up element at given index to maintain heap property
|
|
*/
|
|
private bubbleUp(index: number): void {
|
|
while (index > 0) {
|
|
const parentIndex = Math.floor((index - 1) / 2);
|
|
|
|
if (this.compareFn(this.heap[index], this.heap[parentIndex]) >= 0) {
|
|
break;
|
|
}
|
|
|
|
this.swap(index, parentIndex);
|
|
index = parentIndex;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Bubble down element at given index to maintain heap property
|
|
*/
|
|
private bubbleDown(index: number): void {
|
|
const length = this.heap.length;
|
|
|
|
while (true) {
|
|
const leftChild = 2 * index + 1;
|
|
const rightChild = 2 * index + 2;
|
|
let smallest = index;
|
|
|
|
if (leftChild < length &&
|
|
this.compareFn(this.heap[leftChild], this.heap[smallest]) < 0) {
|
|
smallest = leftChild;
|
|
}
|
|
|
|
if (rightChild < length &&
|
|
this.compareFn(this.heap[rightChild], this.heap[smallest]) < 0) {
|
|
smallest = rightChild;
|
|
}
|
|
|
|
if (smallest === index) break;
|
|
|
|
this.swap(index, smallest);
|
|
index = smallest;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Swap two elements in the heap
|
|
*/
|
|
private swap(i: number, j: number): void {
|
|
const temp = this.heap[i];
|
|
this.heap[i] = this.heap[j];
|
|
this.heap[j] = temp;
|
|
|
|
if (this.keyMap && this.extractKey) {
|
|
this.keyMap.set(this.extractKey(this.heap[i]), i);
|
|
this.keyMap.set(this.extractKey(this.heap[j]), j);
|
|
}
|
|
}
|
|
} |