smartproxy/ts/core/utils/binary-heap.ts
Philipp Kunz 829ae0d6a3 fix(refactor): remove deprecated Port80Handler and related utilities
- 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.
2025-05-31 18:01:09 +00:00

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);
}
}
}