feat: add range search functionality to BST

This commit is contained in:
Revone 2024-11-22 11:23:57 +13:00
parent 713b145a1c
commit 080a671de1
14 changed files with 236 additions and 63 deletions

19
src/common/index.ts Normal file
View file

@ -0,0 +1,19 @@
export enum DFSOperation {
VISIT = 0,
PROCESS = 1
}
export class Range<K> {
constructor(
public low: K,
public high: K,
public includeLow: boolean = true,
public includeHigh: boolean = true
) {}
// Determine whether a key is within the range
isInRange(key: K, comparator: (a: K, b: K) => number): boolean {
const lowCheck = this.includeLow ? comparator(key, this.low) >= 0 : comparator(key, this.low) > 0;
const highCheck = this.includeHigh ? comparator(key, this.high) <= 0 : comparator(key, this.high) < 0;
return lowCheck && highCheck;
}
}

View file

@ -1,4 +0,0 @@
export enum DFSOperation {
VISIT = 0,
PROCESS = 1
}

View file

@ -187,17 +187,14 @@ export class AVLTreeMultiMap<
return [this.createNode(key, finalValue, count), finalValue];
}
if (this.isKey(keyNodeEntryOrRaw)) return [this.createNode(keyNodeEntryOrRaw, value, count), value];
if (this.isRaw(keyNodeEntryOrRaw)) {
if (this._toEntryFn) {
const [key, entryValue] = this._toEntryFn(keyNodeEntryOrRaw as R);
const finalValue = value ?? entryValue;
if (this.isKey(key)) return [this.createNode(key, finalValue, count), finalValue];
}
return [undefined, undefined];
const [key, entryValue] = this._toEntryFn!(keyNodeEntryOrRaw);
const finalValue = value ?? entryValue;
if (this.isKey(key)) return [this.createNode(key, finalValue, count), finalValue];
}
if (this.isKey(keyNodeEntryOrRaw)) return [this.createNode(keyNodeEntryOrRaw, value, count), value];
return [undefined, undefined];
}

View file

@ -29,7 +29,7 @@ import { IBinaryTree } from '../../interfaces';
import { isComparable, trampoline } from '../../utils';
import { Queue } from '../queue';
import { IterableEntryBase } from '../base';
import { DFSOperation } from '../../constants';
import { DFSOperation, Range } from '../../common';
/**
* Represents a node in a binary tree.
@ -233,17 +233,14 @@ export class BinaryTree<
return [this.createNode(key, finalValue), finalValue];
}
if (this.isKey(keyNodeEntryOrRaw)) return [this.createNode(keyNodeEntryOrRaw, value), value];
if (this.isRaw(keyNodeEntryOrRaw)) {
if (this._toEntryFn) {
const [key, entryValue] = this._toEntryFn(keyNodeEntryOrRaw);
const finalValue = value ?? entryValue;
if (this.isKey(key)) return [this.createNode(key, finalValue), finalValue];
}
return [undefined, undefined];
const [key, entryValue] = this._toEntryFn!(keyNodeEntryOrRaw);
const finalValue = value ?? entryValue;
if (this.isKey(key)) return [this.createNode(key, finalValue), finalValue];
}
if (this.isKey(keyNodeEntryOrRaw)) return [this.createNode(keyNodeEntryOrRaw, value), value];
return [undefined, undefined];
}
@ -310,7 +307,7 @@ export class BinaryTree<
* indicating that it is of type `R`.
*/
isRaw(keyNodeEntryOrRaw: BTNRep<K, V, NODE> | R): keyNodeEntryOrRaw is R {
return typeof keyNodeEntryOrRaw === 'object';
return this._toEntryFn !== undefined && typeof keyNodeEntryOrRaw === 'object';
}
/**
@ -352,6 +349,12 @@ export class BinaryTree<
return keyNodeEntryOrRaw === this._NIL;
}
isRange(
keyNodeEntryRawOrPredicate: BTNRep<K, V, NODE> | R | NodePredicate<NODE> | Range<K>
): keyNodeEntryRawOrPredicate is Range<K> {
return keyNodeEntryRawOrPredicate instanceof Range;
}
/**
* The function determines whether a given key, node, entry, or raw data is a leaf node in a binary
* tree.
@ -515,6 +518,14 @@ export class BinaryTree<
return inserted;
}
/**
* Time Complexity: O(k * n)
* Space Complexity: O(1)
*/
merge(anotherTree: BinaryTree<K, V, R, NODE, TREE>) {
this.addMany(anotherTree, []);
}
/**
* Time Complexity: O(k * n)
* Space Complexity: O(1)

View file

@ -23,6 +23,7 @@ import { BinaryTree, BinaryTreeNode } from './binary-tree';
import { IBinaryTree } from '../../interfaces';
import { Queue } from '../queue';
import { isComparable } from '../../utils';
import { Range } from '../../common';
export class BSTNode<K = any, V = any, NODE extends BSTNode<K, V, NODE> = BSTNodeNested<K, V>> extends BinaryTreeNode<
K,
@ -167,8 +168,9 @@ export class BST<
super([], options);
if (options) {
const { comparator } = options;
if (comparator) this._comparator = comparator;
const { comparator, isReverse } = options;
if (isReverse !== undefined) this._isReverse = isReverse;
if (comparator !== undefined) this._comparator = comparator;
}
if (keysNodesEntriesOrRaws) this.addMany(keysNodesEntriesOrRaws);
@ -184,6 +186,12 @@ export class BST<
return this._root;
}
protected _isReverse = false;
get isReverse() {
return this._isReverse;
}
/**
* The function creates a new BSTNode with the given key and value and returns it.
* @param {K} key - The key parameter is of type K, which represents the type of the key for the node
@ -269,11 +277,11 @@ export class BST<
* @param {any} key - The `key` parameter is a value that will be checked to determine if it is of
* type `K`.
* @returns The `override isKey(key: any): key is K` function is returning a boolean value based on
* the result of the `isComparable` function with the condition `this.comparator !==
* the result of the `isComparable` function with the condition `this._compare !==
* this._DEFAULT_COMPARATOR`.
*/
override isKey(key: any): key is K {
return isComparable(key, this.comparator !== this._DEFAULT_COMPARATOR);
return isComparable(key, this._compare !== this._DEFAULT_COMPARATOR);
}
/**
@ -300,11 +308,11 @@ export class BST<
let current = this._root;
while (current !== undefined) {
if (this.comparator(current.key, newNode.key) === 0) {
if (this._compare(current.key, newNode.key) === 0) {
this._replaceNode(current, newNode);
if (this._isMapMode) this._setValue(current.key, newValue);
return true;
} else if (this.comparator(current.key, newNode.key) > 0) {
} else if (this._compare(current.key, newNode.key) > 0) {
if (current.left === undefined) {
current.left = newNode;
if (this._isMapMode) this._setValue(newNode?.key, newValue);
@ -402,7 +410,7 @@ export class BST<
}
if (keyA !== undefined && keyA !== null && keyB !== undefined && keyB !== null) {
return this.comparator(keyA, keyB);
return this._compare(keyA, keyB);
}
return 0;
});
@ -444,6 +452,14 @@ export class BST<
return inserted;
}
/**
* Time Complexity: O(k * n)
* Space Complexity: O(1)
*/
override merge(anotherTree: BST<K, V, R, NODE, TREE>) {
this.addMany(anotherTree, [], false);
}
/**
* Time Complexity: O(log n)
* Space Complexity: O(k + log n)
@ -473,7 +489,7 @@ export class BST<
* collected in an array and returned as the output of the method.
*/
override search<C extends NodeCallback<NODE>>(
keyNodeEntryRawOrPredicate: BTNRep<K, V, NODE> | R | NodePredicate<NODE>,
keyNodeEntryRawOrPredicate: BTNRep<K, V, NODE> | R | NodePredicate<NODE> | Range<K>,
onlyOne = false,
callback: C = this._DEFAULT_NODE_CALLBACK as C,
startNode: BTNRep<K, V, NODE> | R = this._root,
@ -483,9 +499,36 @@ export class BST<
if (keyNodeEntryRawOrPredicate === null) return [];
startNode = this.ensureNode(startNode);
if (!startNode) return [];
const predicate = this._ensurePredicate(keyNodeEntryRawOrPredicate);
const ans: ReturnType<C>[] = [];
let predicate: NodePredicate<NODE>;
const isRange = this.isRange(keyNodeEntryRawOrPredicate);
// Set predicate based on parameter type
if (isRange) {
predicate = node => keyNodeEntryRawOrPredicate.isInRange(node.key, this._comparator);
} else {
predicate = this._ensurePredicate(keyNodeEntryRawOrPredicate);
}
const isToLeftByRange = (cur: NODE) => {
if (isRange) {
const range = keyNodeEntryRawOrPredicate;
const leftS = this.isReverse ? range.high : range.low;
const leftI = this.isReverse ? range.includeHigh : range.includeLow;
return (leftI && this._compare(cur.key, leftS) >= 0) || (!leftI && this._compare(cur.key, leftS) > 0);
}
return false;
};
const isToRightByRange = (cur: NODE) => {
if (isRange) {
const range = keyNodeEntryRawOrPredicate;
const rightS = this.isReverse ? range.low : range.high;
const rightI = this.isReverse ? range.includeLow : range.includeLow;
return (rightI && this._compare(cur.key, rightS) <= 0) || (!rightI && this._compare(cur.key, rightS) < 0);
}
return false;
};
const ans: ReturnType<C>[] = [];
if (iterationType === 'RECURSIVE') {
const dfs = (cur: NODE) => {
if (predicate(cur)) {
@ -494,20 +537,24 @@ export class BST<
}
if (!this.isRealNode(cur.left) && !this.isRealNode(cur.right)) return;
if (!this._isPredicate(keyNodeEntryRawOrPredicate)) {
if (isRange) {
if (this.isRealNode(cur.left) && isToLeftByRange(cur)) dfs(cur.left);
if (this.isRealNode(cur.right) && isToRightByRange(cur)) dfs(cur.right);
} else if (!this._isPredicate(keyNodeEntryRawOrPredicate)) {
const benchmarkKey = this._extractKey(keyNodeEntryRawOrPredicate);
if (
this.isRealNode(cur.left) &&
benchmarkKey !== null &&
benchmarkKey !== undefined &&
this.comparator(cur.key, benchmarkKey) > 0
this._compare(cur.key, benchmarkKey) > 0
)
dfs(cur.left);
if (
this.isRealNode(cur.right) &&
benchmarkKey !== null &&
benchmarkKey !== undefined &&
this.comparator(cur.key, benchmarkKey) < 0
this._compare(cur.key, benchmarkKey) < 0
)
dfs(cur.right);
} else {
@ -525,20 +572,23 @@ export class BST<
ans.push(callback(cur));
if (onlyOne) return ans;
}
if (!this._isPredicate(keyNodeEntryRawOrPredicate)) {
if (isRange) {
if (this.isRealNode(cur.left) && isToLeftByRange(cur)) stack.push(cur.left);
if (this.isRealNode(cur.right) && isToRightByRange(cur)) stack.push(cur.right);
} else if (!this._isPredicate(keyNodeEntryRawOrPredicate)) {
const benchmarkKey = this._extractKey(keyNodeEntryRawOrPredicate);
if (
this.isRealNode(cur.right) &&
benchmarkKey !== null &&
benchmarkKey !== undefined &&
this.comparator(cur.key, benchmarkKey) < 0
this._compare(cur.key, benchmarkKey) < 0
)
stack.push(cur.right);
if (
this.isRealNode(cur.left) &&
benchmarkKey !== null &&
benchmarkKey !== undefined &&
this.comparator(cur.key, benchmarkKey) > 0
this._compare(cur.key, benchmarkKey) > 0
)
stack.push(cur.left);
} else {
@ -712,7 +762,7 @@ export class BST<
if (iterationType === 'RECURSIVE') {
const dfs = (cur: NODE) => {
const compared = this.comparator(cur.key, targetKey);
const compared = this._compare(cur.key, targetKey);
if (Math.sign(compared) === lesserOrGreater) ans.push(callback(cur));
if (this.isRealNode(cur.left)) dfs(cur.left);
@ -726,7 +776,7 @@ export class BST<
while (queue.size > 0) {
const cur = queue.shift();
if (this.isRealNode(cur)) {
const compared = this.comparator(cur.key, targetKey);
const compared = this._compare(cur.key, targetKey);
if (Math.sign(compared) === lesserOrGreater) ans.push(callback(cur));
if (this.isRealNode(cur.left)) queue.push(cur.left);
@ -876,4 +926,8 @@ export class BST<
}
this._root = v;
}
protected _compare(a: K, b: K) {
return this._isReverse ? -this._comparator(a, b) : this._comparator(a, b);
}
}

View file

@ -51,6 +51,10 @@ export class RedBlackTreeNode<
}
}
/**
* 1. Efficient self-balancing, but not completely balanced. Compared with AVLTree, the addition and deletion efficiency is high but the query efficiency is slightly lower.
* 2. It is BST itself. Compared with Heap which is not completely ordered, RedBlackTree is completely ordered.
*/
export class RedBlackTree<
K = any,
V = any,
@ -351,7 +355,7 @@ export class RedBlackTree<
while (this.isRealNode(current)) {
parent = current;
const compared = this.comparator(node.key, current.key);
const compared = this._compare(node.key, current.key);
if (compared < 0) {
current = current.left ?? this.NIL;
} else if (compared > 0) {

View file

@ -173,8 +173,8 @@ export class TreeMultiMap<
if (this.isKey(key)) return [this.createNode(key, finalValue, 'BLACK', count), finalValue];
}
if (this._toEntryFn) {
const [key, entryValue] = this._toEntryFn(keyNodeEntryOrRaw as R);
if (this.isRaw(keyNodeEntryOrRaw)) {
const [key, entryValue] = this._toEntryFn!(keyNodeEntryOrRaw);
const finalValue = value ?? entryValue;
if (this.isKey(key)) return [this.createNode(key, finalValue, 'BLACK', count), finalValue];
}

View file

@ -2,4 +2,4 @@ export * from './data-structures';
export * from './utils';
export * from './interfaces';
export * from './types';
export * from './constants';
export * from './common';

View file

@ -23,3 +23,5 @@ export type OptValue<V> = V | undefined;
export type IterableWithSizeOrLength<T> = IterableWithSize<T> | IterableWithLength<T>;
export type CRUD = 'CREATED' | 'READ' | 'UPDATED' | 'DELETED';
export type Arithmetic = number | bigint;

View file

@ -1,6 +1,6 @@
import { BinaryTree, BinaryTreeNode } from '../../../data-structures';
import { IterationType, OptValue } from '../../common';
import { DFSOperation } from '../../../constants';
import { DFSOperation } from '../../../common';
export type BinaryTreeNodeNested<K, V> = BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, BinaryTreeNode<K, V, any>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

View file

@ -7,7 +7,8 @@ export type BSTNodeNested<K, V> = BSTNode<K, V, BSTNode<K, V, BSTNode<K, V, BSTN
export type BSTNested<K, V, R, NODE extends BSTNode<K, V, NODE>> = BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, BST<K, V, R, NODE, any>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
export type BSTOptions<K, V, R> = BinaryTreeOptions<K, V, R> & {
comparator?: Comparator<K>
comparator?: Comparator<K>;
isReverse?: boolean;
}
export type BSTNOptKey<K> = K | undefined;

View file

@ -7,4 +7,4 @@ export type RedBlackTreeNodeNested<K, V> = RedBlackTreeNode<K, V, RedBlackTreeNo
export type RedBlackTreeNested<K, V, R, NODE extends RedBlackTreeNode<K, V, NODE>> = RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, RedBlackTree<K, V, R, NODE, any>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
export type RBTreeOptions<K, V, R> = BSTOptions<K, V, R> & {};
export type RBTreeOptions<K, V, R> = Omit<BSTOptions<K, V, R>, 'isReverse'> & {};

View file

@ -1,4 +1,4 @@
import { BinaryTreeNode, BST, BSTNode } from '../../../../src';
import { BinaryTreeNode, BST, BSTNode, Range } from '../../../../src';
import { isDebugTest, isTestStackOverflow, SYSTEM_MAX_CALL_STACK } from '../../../config';
const isDebug = isDebugTest;
@ -1550,20 +1550,10 @@ describe('classic use', () => {
// Test case for finding elements in a given range
it('@example Find elements in a range', () => {
const bst = new BST<number>([10, 5, 15, 3, 7, 12, 18]);
// Helper function to find elements in range
const findElementsInRange = (min: number, max: number): number[] => {
return bst.search(
node => node.key >= min && node.key <= max,
false,
node => node.key
);
};
// Assertions
expect(findElementsInRange(4, 12)).toEqual([10, 5, 7, 12]);
expect(findElementsInRange(15, 20)).toEqual([15, 18]);
const bst = new BST<number>([10, 5, 15, 3, 7, 12, 18], { isReverse: true });
expect(bst.search(new Range(5, 10))).toEqual([10, 5, 7]);
expect(bst.search(new Range(4, 12))).toEqual([10, 5, 7, 12]);
expect(bst.search(new Range(15, 20))).toEqual([15, 18]);
});
// Test case for Huffman coding simulation

View file

@ -1,4 +1,4 @@
import { BinaryTreeNode, BSTNode, RedBlackTree, RedBlackTreeNode } from '../../../../src';
import { BinaryTreeNode, BSTNode, Range, RedBlackTree, RedBlackTreeNode } from '../../../../src';
import { getRandomInt, getRandomIntArray, magnitude } from '../../../utils';
import { OrderedMap } from 'js-sdsl';
@ -819,3 +819,102 @@ describe('RedBlackTree - _deleteFixup', () => {
]);
});
});
describe('classic use', () => {
it('Database Index: Add, Search, and Delete Records', () => {
const dbIndex = new RedBlackTree<number, string>();
// Insert records
dbIndex.add(1, 'Alice');
dbIndex.add(2, 'Bob');
dbIndex.add(3, 'Charlie');
// Search for records
expect(dbIndex.get(1)).toBe('Alice');
expect(dbIndex.get(2)).toBe('Bob');
expect(dbIndex.get(3)).toBe('Charlie');
// Delete a record
dbIndex.delete(2);
expect(dbIndex.get(2)).toBeUndefined();
});
it('@example Merge 3 sorted datasets', () => {
const dataset1 = new RedBlackTree<number, string>([
[1, 'A'],
[7, 'G']
]);
const dataset2 = [
[2, 'B'],
[6, 'F']
];
const dataset3 = new RedBlackTree<number, string>([
[3, 'C'],
[5, 'E'],
[4, 'D']
]);
// Merge datasets into a single Red-Black Tree
const merged = new RedBlackTree<number, string>(dataset1);
merged.addMany(dataset2);
merged.merge(dataset3);
// Verify merged dataset is in sorted order
expect([...merged.values()]).toEqual(['A', 'B', 'C', 'D', 'E', 'F', 'G']);
});
// Test case for finding elements in a given range
it('Find elements in a range', () => {
const bst = new RedBlackTree<number>([10, 5, 15, 3, 7, 12, 18]);
expect(bst.search(new Range(5, 10))).toEqual([5, 10, 7]);
expect(bst.search(new Range(4, 12))).toEqual([5, 10, 12, 7]);
expect(bst.search(new Range(15, 20))).toEqual([15, 18]);
});
it('Timer List: Manage Timed Tasks', () => {
const timerList = new RedBlackTree<number, string>(); // Key: Time in ms, Value: Task Name
// Schedule tasks
timerList.add(100, 'Task A');
timerList.add(200, 'Task B');
timerList.add(50, 'Task C');
// Verify the order of tasks by retrieval
expect([...timerList.values()]).toEqual(['Task C', 'Task A', 'Task B']); // Sorted by key (time)
// Remove the earliest task
timerList.delete(50);
expect([...timerList.values()]).toEqual(['Task A', 'Task B']);
});
it('Scheduler: Manage Tasks by Priority', () => {
const scheduler = new RedBlackTree<number, string>(); // Key: Priority, Value: Task Name
// Add tasks with different priorities
scheduler.add(3, 'Low Priority Task');
scheduler.add(1, 'High Priority Task');
scheduler.add(2, 'Medium Priority Task');
// Verify the order of tasks by retrieval
expect([...scheduler.values()]).toEqual(['High Priority Task', 'Medium Priority Task', 'Low Priority Task']);
// Remove the highest priority task
scheduler.delete(1);
expect([...scheduler.values()]).toEqual(['Medium Priority Task', 'Low Priority Task']);
});
it('Routing Table: Manage IP Routes', () => {
const routingTable = new RedBlackTree<number, string>(); // Key: IP Address, Value: Route
// Add routes
routingTable.add(1921680101, 'Route A');
routingTable.add(1921680102, 'Route B');
routingTable.add(1921680100, 'Route C');
// Search for a specific route
expect(routingTable.get(1921680101)).toBe('Route A');
// Verify all routes in sorted order
expect([...routingTable.values()]).toEqual(['Route C', 'Route A', 'Route B']);
});
});