mirror of
https://github.com/zrwusa/data-structure-typed.git
synced 2024-11-23 04:44:04 +00:00
feat: add range search functionality to BST
This commit is contained in:
parent
713b145a1c
commit
080a671de1
19
src/common/index.ts
Normal file
19
src/common/index.ts
Normal 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;
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
export enum DFSOperation {
|
||||
VISIT = 0,
|
||||
PROCESS = 1
|
||||
}
|
|
@ -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];
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
|
|
@ -2,4 +2,4 @@ export * from './data-structures';
|
|||
export * from './utils';
|
||||
export * from './interfaces';
|
||||
export * from './types';
|
||||
export * from './constants';
|
||||
export * from './common';
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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'> & {};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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']);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue