diff --git a/src/data-structures/binary-tree/binary-tree.ts b/src/data-structures/binary-tree/binary-tree.ts index 575bdcf..d02b777 100644 --- a/src/data-structures/binary-tree/binary-tree.ts +++ b/src/data-structures/binary-tree/binary-tree.ts @@ -1048,6 +1048,26 @@ export class BinaryTree = BinaryTreeNode } } + /** + * The function `getSuccessor` returns the next node in a binary tree given a node `x`, or `null` if + * `x` is the last node. + * @param {N} x - N - a node in a binary tree + * @returns The function `getSuccessor` returns a value of type `N` (the successor node), or `null` + * if there is no successor, or `undefined` if the input `x` is `undefined`. + */ + getSuccessor(x: N): N | null | undefined{ + if (x.right) { + return this.getLeftMost(x.right); + } + + let y: N | null | undefined = x.parent; + while (y && y && x === y.right) { + x = y; + y = y.parent; + } + return y; + } + // --- start additional methods --- /** diff --git a/src/data-structures/binary-tree/rb-tree.ts b/src/data-structures/binary-tree/rb-tree.ts index 3018dc7..9009419 100644 --- a/src/data-structures/binary-tree/rb-tree.ts +++ b/src/data-structures/binary-tree/rb-tree.ts @@ -1,358 +1,426 @@ -import {BTNKey, RBColor, RBTreeNodeNested, RBTreeOptions} from '../../types'; -import {IBinaryTree} from '../../interfaces'; -import {BST, BSTNode} from './bst'; +export enum RBTNColor {BLACK, RED} -export class RBTreeNode = RBTreeNodeNested> extends BSTNode { - constructor(key: BTNKey, value?: V) { - super(key, value); - this.color = RBColor.RED; +export class RBTreeNode { + key: number = 0; + parent: RBTreeNode; + left: RBTreeNode; + right: RBTreeNode; + color: number = RBTNColor.BLACK; + + constructor() { + this.parent = null as unknown as RBTreeNode; + this.left = null as unknown as RBTreeNode; + this.right = null as unknown as RBTreeNode; } - - color: RBColor; - } -export class RBTree = RBTreeNode>> - extends BST - implements IBinaryTree { - constructor(options?: RBTreeOptions) { - super(options); +export class RedBlackTree { + + constructor() { + this._NIL = new RBTreeNode(); + this.NIL.color = RBTNColor.BLACK; + this.NIL.left = null as unknown as RBTreeNode; + this.NIL.right = null as unknown as RBTreeNode; + this._root = this.NIL; } - override createNode(key: BTNKey, value?: V): N { - return new RBTreeNode(key, value) as N; + protected _root: RBTreeNode; + + get root(): RBTreeNode { + return this._root; } - // override add(keyOrNode: BTNKey | N | null, value?: V): N | null | undefined { - // const inserted = super.add(keyOrNode, value); - // if (inserted) this._fixInsertViolation(inserted); - // return inserted; - // } - // - // // Method for fixing insert violations in a red-black tree - // protected _fixInsertViolation(node: N) { - // while (node !== this.root! && node.color === RBColor.RED && node.parent!.color === RBColor.RED) { - // const parent = node.parent!; - // const grandparent = parent.parent!; - // let uncle: N | null | undefined = null; - // - // if (parent === grandparent.left) { - // uncle = grandparent.right; - // - // // Case 1: The uncle node is red - // if (uncle && uncle.color === RBColor.RED) { - // grandparent.color = RBColor.RED; - // parent.color = RBColor.BLACK; - // uncle.color = RBColor.BLACK; - // node = grandparent; - // } else { - // // Case 2: The uncle node is black, and the current node is a right child - // if (node === parent.right) { - // this._rotateLeft(parent); - // node = parent; - // // Update parent reference - // node.parent = grandparent; - // parent.parent = node; - // } - // - // // Case 3: The uncle node is black, and the current node is a left child - // parent.color = RBColor.BLACK; - // grandparent.color = RBColor.RED; - // this._rotateRight(grandparent); - // } - // } else { - // // Symmetric case: The parent is the right child of the grandparent - // uncle = grandparent.left; - // - // // Case 1: The uncle node is red - // if (uncle && uncle.color === RBColor.RED) { - // grandparent.color = RBColor.RED; - // parent.color = RBColor.BLACK; - // uncle.color = RBColor.BLACK; - // node = grandparent; - // } else { - // // Case 2: The uncle node is black, and the current node is a left child - // if (node === parent.left) { - // this._rotateRight(parent); - // node = parent; - // // Update parent reference - // node.parent = grandparent; - // parent.parent = node; - // } - // - // // Case 3: The uncle node is black, and the current node is a right child - // parent.color = RBColor.BLACK; - // grandparent.color = RBColor.RED; - // this._rotateLeft(grandparent); - // } - // } - // } - // - // // The root node is always black - // this.root!.color = RBColor.BLACK; - // } - // - // // Left rotation operation - // protected _rotateLeft(node: N) { - // const rightChild = node.right; - // node.right = rightChild!.left; - // - // if (rightChild!.left) { - // rightChild!.left.parent = node; - // } - // - // rightChild!.parent = node.parent; - // - // if (node === this.root) { - // // @ts-ignore - // this._setRoot(rightChild); - // } else if (node === node.parent!.left) { - // node.parent!.left = rightChild; - // } else { - // node.parent!.right = rightChild; - // } - // - // rightChild!.left = node; - // node.parent = rightChild; - // } - // - // // Right rotation operation - // protected _rotateRight(node: N) { - // const leftChild = node.left; - // node.left = leftChild!.right; - // - // if (leftChild!.right) { - // leftChild!.right.parent = node; - // } - // - // leftChild!.parent = node.parent; - // - // if (node === this.root) { - // // @ts-ignore - // this._setRoot(leftChild); - // } else if (node === node.parent!.right) { - // node.parent!.right = leftChild; - // } else { - // node.parent!.left = leftChild; - // } - // - // leftChild!.right = node; - // node.parent = leftChild; - // } - // - // protected _isNodeRed(node: N | null | undefined): boolean { - // return node ? node.color === RBColor.RED : false; - // } - // - // // Find the sibling node - // protected _findSibling(node: N): N | null | undefined { - // if (!node.parent) { - // return undefined; - // } - // - // return node === node.parent.left ? node.parent.right : node.parent.left; - // } - // - // // Remove a node - // protected _removeNode(node: N, replacement: N | null | undefined): void { - // if (node === this.root && !replacement) { - // // If there's only the root node and no replacement, simply delete the root node - // this._setRoot(null); - // } else if (node === this.root || this._isNodeRed(node)) { - // // If the node is the root or a red node, delete it directly - // if (node.parent!.left === node) { - // node.parent!.left = replacement; - // } else { - // node.parent!.right = replacement; - // } - // - // if (replacement) { - // replacement.parent = node.parent!; - // replacement.color = RBColor.BLACK; // Set the replacement node's color to black - // } - // } else { - // // If the node is a black node, perform removal and repair - // const sibling = this._findSibling(node); - // - // if (node.parent!.left === node) { - // node.parent!.left = replacement; - // } else { - // node.parent!.right = replacement; - // } - // - // if (replacement) { - // replacement.parent = node.parent; - // } - // - // if (!this._isNodeRed(sibling)) { - // // If the sibling node is black, perform repair - // this._fixDeleteViolation(replacement || node); - // } - // } - // - // if (node.parent) { - // node.parent = null; - // } - // node.left = null; - // node.right = null; - // } - // - // override delete(keyOrNode: BTNKey | N): BinaryTreeDeletedResult[] { - // const node = this.get(keyOrNode); - // const result: BinaryTreeDeletedResult[] = [{deleted: undefined, needBalanced: null}]; - // if (!node) return result; // Node does not exist - // - // const replacement = this._getReplacementNode(node); - // - // const isRed = this._isNodeRed(node); - // const isRedReplacement = this._isNodeRed(replacement); - // - // // Remove the node - // this._removeNode(node, replacement); - // - // if (isRed || isRedReplacement) { - // // If the removed node is red or the replacement node is red, no repair is needed - // return result; - // } - // - // // Repair any violation introduced by the removal - // this._fixDeleteViolation(replacement); - // - // return result; - // } - // - // // Repair operation after node deletion - // protected _fixDeleteViolation(node: N | null | undefined) { - // let sibling; - // - // while (node && node !== this.root && !this._isNodeRed(node)) { - // if (node === node.parent!.left) { - // sibling = node.parent!.right; - // - // if (sibling && this._isNodeRed(sibling)) { - // // Case 1: The sibling node is red - // sibling.color = RBColor.BLACK; - // node.parent!.color = RBColor.RED; - // this._rotateLeft(node.parent!); - // sibling = node.parent!.right; - // } - // - // if (!sibling) return; - // - // if ( - // (!sibling.left || sibling.left.color === RBColor.BLACK) && - // (!sibling.right || sibling.right.color === RBColor.BLACK) - // ) { - // // Case 2: The sibling node and its children are all black - // sibling.color = RBColor.RED; - // node = node.parent!; - // } else { - // if (!(sibling.right && this._isNodeRed(sibling.right))) { - // // Case 3: The sibling node is black, and the left child is red, the right child is black - // sibling.left!.color = RBColor.BLACK; - // sibling.color = RBColor.RED; - // this._rotateRight(sibling); - // sibling = node.parent!.right; - // } - // - // // Case 4: The sibling node is black, and the right child is red - // if (sibling) { - // sibling.color = node.parent!.color; - // } - // if (node.parent) { - // node.parent.color = RBColor.BLACK; - // } - // if (sibling!.right) { - // sibling!.right.color = RBColor.BLACK; - // } - // this._rotateLeft(node.parent!); - // node = this.root; - // } - // } else { - // // Symmetric case: The parent is the right child of the grandparent - // sibling = node.parent!.left; - // - // if (sibling && this._isNodeRed(sibling)) { - // // Case 1: The sibling node is red - // sibling.color = RBColor.BLACK; - // node.parent!.color = RBColor.RED; - // this._rotateRight(node.parent!); - // sibling = node.parent!.left; - // } - // - // if (!sibling) return; - // - // if ( - // (!sibling.left || sibling.left.color === RBColor.BLACK) && - // (!sibling.right || sibling.right.color === RBColor.BLACK) - // ) { - // // Case 2: The sibling node and its children are all black - // sibling.color = RBColor.RED; - // node = node.parent!; - // } else { - // if (!(sibling.left && this._isNodeRed(sibling.left))) { - // // Case 3: The sibling node is black, and the right child is red, the left child is black - // sibling.right!.color = RBColor.BLACK; - // sibling.color = RBColor.RED; - // this._rotateLeft(sibling); - // sibling = node.parent!.left; - // } - // - // // Case 4: The sibling node is black, and the left child is red - // if (sibling) { - // sibling.color = node.parent!.color; - // } - // if (node.parent) { - // node.parent.color = RBColor.BLACK; - // } - // if (sibling!.left) { - // sibling!.left.color = RBColor.BLACK; - // } - // this._rotateRight(node.parent!); - // node = this.root; - // } - // } - // } - // - // if (node) { - // node.color = RBColor.BLACK; - // } - // } - // - // protected _findMin(node: N): N { - // while (node.left) { - // node = node.left; - // } - // return node; - // } - // - // // Get the replacement node - // protected _getReplacementNode(node: N): N | null | undefined { - // if (node.left && node.right) { - // return this._findSuccessor(node); - // } - // - // if (!node.left && !node.right) { - // return null; // Return a fake node with color black - // } - // - // return node.left || node.right; - // } - // - // // Find the successor node - // protected _findSuccessor(node: N): N | null | undefined { - // if (node.right) { - // // If the node has a right child, find the minimum node in the right subtree as the successor - // return this._findMin(node.right); - // } - // - // // Otherwise, traverse upward until finding the first parent whose left child is the current node - // let parent = node.parent; - // while (parent && node === parent.right) { - // node = parent; - // parent = parent.parent; - // } - // - // return parent; - // } -} + protected _NIL: RBTreeNode; + + get NIL(): RBTreeNode { + return this._NIL; + } + + /** + * The `insert` function inserts a new node with a given key into a red-black tree and fixes any + * violations of the red-black tree properties. + * @param {number} key - The key parameter is a number that represents the value to be inserted into + * the RBTree. + * @returns The function does not explicitly return anything. + */ + insert(key: number): void { + + const node: RBTreeNode = new RBTreeNode(); + node.parent = null as unknown as RBTreeNode; + node.key = key; + node.left = this.NIL; + node.right = this.NIL; + node.color = RBTNColor.RED; + + let y: RBTreeNode = null as unknown as RBTreeNode; + let x: RBTreeNode = this.root; + + while (x !== this.NIL) { + y = x; + if (node.key < x.key) { + x = x.left; + } else { + x = x.right; + } + } + + node.parent = y; + if (y === null) { + this._root = node; + } else if (node.key < y.key) { + y.left = node; + } else { + y.right = node; + } + + if (node.parent === null) { + node.color = RBTNColor.BLACK; + return; + } + + if (node.parent.parent === null) { + return; + } + + this._fixInsert(node); + } + + /** + * The `delete` function in TypeScript is used to remove a node with a specific key from a red-black + * tree. + * @param {RBTreeNode} node - The `node` parameter is of type `RBTreeNode` and represents the current + * node being processed in the delete operation. + * @returns The `delete` function does not return anything. It has a return type of `void`. + */ + delete(key: number): void { + const helper = (node: RBTreeNode): void => { + + let z: RBTreeNode = this.NIL; + let x: RBTreeNode, y: RBTreeNode; + while (node !== this.NIL) { + if (node.key === key) { + z = node; + } + + if (node.key <= key) { + node = node.right; + } else { + node = node.left; + } + } + + if (z === this.NIL) { + console.log("Couldn't find key in the tree"); + return; + } + + y = z; + let yOriginalColor: number = y.color; + if (z.left === this.NIL) { + x = z.right; + this._rbTransplant(z, z.right); + } else if (z.right === this.NIL) { + x = z.left; + this._rbTransplant(z, z.left); + } else { + y = this.getLeftMost(z.right); + yOriginalColor = y.color; + x = y.right; + if (y.parent === z) { + x.parent = y; + } else { + this._rbTransplant(y, y.right); + y.right = z.right; + y.right.parent = y; + } + + this._rbTransplant(z, y); + y.left = z.left; + y.left.parent = y; + y.color = z.color; + } + if (yOriginalColor === 0) { + this.fixDelete(x); + } + } + helper(this.root); + } + + /** + * The function `getNode` is a recursive depth-first search algorithm that searches for a node with a + * given key in a red-black tree. + * @param {number} key - The key parameter is a number that represents the value we are searching for + * in the RBTree. + * @param beginRoot - The `beginRoot` parameter is an optional parameter that represents the starting + * point for the search in the binary search tree. If no value is provided for `beginRoot`, it + * defaults to the root of the binary search tree (`this.root`). + * @returns a RBTreeNode. + */ + getNode(key: number, beginRoot = this.root): RBTreeNode { + const dfs = (node: RBTreeNode): RBTreeNode => { + if (node === this.NIL || key === node.key) { + return node; + } + + if (key < node.key) { + return dfs(node.left); + } + return dfs(node.right); + } + return dfs(beginRoot); + } + + + /** + * The function returns the leftmost node in a red-black tree. + * @param {RBTreeNode} node - The parameter "node" is of type RBTreeNode, which represents a node in + * a Red-Black Tree. + * @returns The leftmost node in the given RBTreeNode. + */ + getLeftMost(node: RBTreeNode): RBTreeNode { + while (node.left !== null && node.left !== this.NIL) { + node = node.left; + } + return node; + } + + + /** + * The function returns the rightmost node in a red-black tree. + * @param {RBTreeNode} node - The parameter "node" is of type RBTreeNode. + * @returns the rightmost node in a red-black tree. + */ + getRightMost(node: RBTreeNode): RBTreeNode { + while (node.right !== null && node.right !== this.NIL) { + node = node.right; + } + return node; + } + + /** + * The function returns the successor of a given node in a red-black tree. + * @param {RBTreeNode} x - RBTreeNode - The node for which we want to find the successor. + * @returns the successor of the given RBTreeNode. + */ + getSuccessor(x: RBTreeNode): RBTreeNode { + + if (x.right !== this.NIL) { + return this.getLeftMost(x.right); + } + + let y: RBTreeNode = x.parent; + while (y !== this.NIL && y !== null && x === y.right) { + x = y; + y = y.parent; + } + return y; + } + + /** + * The function returns the predecessor of a given node in a red-black tree. + * @param {RBTreeNode} x - The parameter `x` is of type `RBTreeNode`, which represents a node in a + * Red-Black Tree. + * @returns the predecessor of the given RBTreeNode 'x'. + */ + getPredecessor(x: RBTreeNode): RBTreeNode { + + if (x.left !== this.NIL) { + return this.getRightMost(x.left); + } + + let y: RBTreeNode = x.parent; + while (y !== this.NIL && x === y.left) { + x = y; + y = y.parent; + } + + return y; + } + + /** + * The function performs a left rotation on a red-black tree node. + * @param {RBTreeNode} x - The parameter `x` is a RBTreeNode object. + */ + protected _leftRotate(x: RBTreeNode): void { + const y: RBTreeNode = x.right; + x.right = y.left; + if (y.left !== this.NIL) { + y.left.parent = x; + } + y.parent = x.parent; + if (x.parent === null) { + this._root = y; + } else if (x === x.parent.left) { + x.parent.left = y; + } else { + x.parent.right = y; + } + y.left = x; + x.parent = y; + } + + /** + * The function performs a right rotation on a red-black tree node. + * @param {RBTreeNode} x - x is a RBTreeNode, which represents the node that needs to be right + * rotated. + */ + protected _rightRotate(x: RBTreeNode): void { + const y: RBTreeNode = x.left; + x.left = y.right; + if (y.right !== this.NIL) { + y.right.parent = x; + } + y.parent = x.parent; + if (x.parent === null) { + this._root = y; + } else if (x === x.parent.right) { + x.parent.right = y; + } else { + x.parent.left = y; + } + y.right = x; + x.parent = y; + } + + /** + * The fixDelete function is used to rebalance the Red-Black Tree after a node deletion. + * @param {RBTreeNode} x - The parameter `x` is of type `RBTreeNode`, which represents a node in a + * red-black tree. + */ + protected fixDelete(x: RBTreeNode): void { + let s: RBTreeNode; + while (x !== this.root && x.color === 0) { + if (x === x.parent.left) { + s = x.parent.right; + if (s.color === 1) { + + s.color = RBTNColor.BLACK; + x.parent.color = RBTNColor.RED; + this._leftRotate(x.parent); + s = x.parent.right; + } + + if (s.left.color === 0 && s.right.color === 0) { + + s.color = RBTNColor.RED; + x = x.parent; + } else { + if (s.right.color === 0) { + + s.left.color = RBTNColor.BLACK; + s.color = RBTNColor.RED; + this._rightRotate(s); + s = x.parent.right; + } + + s.color = x.parent.color; + x.parent.color = RBTNColor.BLACK; + s.right.color = RBTNColor.BLACK; + this._leftRotate(x.parent); + x = this.root; + } + } else { + s = x.parent.left; + if (s.color === 1) { + + s.color = RBTNColor.BLACK; + x.parent.color = RBTNColor.RED; + this._rightRotate(x.parent); + s = x.parent.left; + } + + if (s.right.color === 0 && s.right.color === 0) { + + s.color = RBTNColor.RED; + x = x.parent; + } else { + if (s.left.color === 0) { + + s.right.color = RBTNColor.BLACK; + s.color = RBTNColor.RED; + this._leftRotate(s); + s = x.parent.left; + } + + s.color = x.parent.color; + x.parent.color = RBTNColor.BLACK; + s.left.color = RBTNColor.BLACK; + this._rightRotate(x.parent); + x = this.root; + } + } + } + x.color = RBTNColor.BLACK; + } + + /** + * The function `_rbTransplant` replaces one node in a red-black tree with another node. + * @param {RBTreeNode} u - The parameter "u" represents a RBTreeNode object. + * @param {RBTreeNode} v - The parameter "v" is a RBTreeNode object. + */ + protected _rbTransplant(u: RBTreeNode, v: RBTreeNode): void { + if (u.parent === null) { + this._root = v; + } else if (u === u.parent.left) { + u.parent.left = v; + } else { + u.parent.right = v; + } + v.parent = u.parent; + } + + /** + * The `_fixInsert` function is used to fix the red-black tree after an insertion operation. + * @param {RBTreeNode} k - The parameter `k` is a RBTreeNode object, which represents a node in a + * red-black tree. + */ + protected _fixInsert(k: RBTreeNode): void { + let u: RBTreeNode; + while (k.parent.color === 1) { + if (k.parent === k.parent.parent.right) { + u = k.parent.parent.left; + if (u.color === 1) { + + u.color = RBTNColor.BLACK; + k.parent.color = RBTNColor.BLACK; + k.parent.parent.color = RBTNColor.RED; + k = k.parent.parent; + } else { + if (k === k.parent.left) { + + k = k.parent; + this._rightRotate(k); + } + + k.parent.color = RBTNColor.BLACK; + k.parent.parent.color = RBTNColor.RED; + this._leftRotate(k.parent.parent); + } + } else { + u = k.parent.parent.right; + + if (u.color === 1) { + + u.color = RBTNColor.BLACK; + k.parent.color = RBTNColor.BLACK; + k.parent.parent.color = RBTNColor.RED; + k = k.parent.parent; + } else { + if (k === k.parent.right) { + + k = k.parent; + this._leftRotate(k); + } + + k.parent.color = RBTNColor.BLACK; + k.parent.parent.color = RBTNColor.RED; + this._rightRotate(k.parent.parent); + } + } + if (k === this.root) { + break; + } + } + this.root.color = RBTNColor.BLACK; + } +} \ No newline at end of file diff --git a/test/unit/data-structures/binary-tree/rb-tree.test.ts b/test/unit/data-structures/binary-tree/rb-tree.test.ts index 43d817f..fd76114 100644 --- a/test/unit/data-structures/binary-tree/rb-tree.test.ts +++ b/test/unit/data-structures/binary-tree/rb-tree.test.ts @@ -1,109 +1,136 @@ -import {RBTree, RBTreeNode} from '../../../../src'; +import { RedBlackTree, RBTreeNode } from '../../../../src'; -describe('RBTreeNode', () => { - it('should create an instance of RBTreeNode', () => { - const node = new RBTreeNode(1); - expect(node).toBeInstanceOf(RBTreeNode); - }); - - it('should set and get the ID correctly', () => { - const node = new RBTreeNode(1); - expect(node.key).toBe(1); - - node.key = 2; - expect(node.key).toBe(2); - }); - - it('should set and get the value correctly', () => { - const node: RBTreeNode = new RBTreeNode(1, 42); - expect(node.value).toBe(42); - - node.value = 55; - expect(node.value).toBe(55); - }); - - it('should set and get the left child correctly', () => { - const node1 = new RBTreeNode(1); - const node2 = new RBTreeNode(2); - - node1.left = node2; - - expect(node1.left).toBe(node2); - expect(node2.parent).toBe(node1); - }); - - it('should set and get the right child correctly', () => { - const node1 = new RBTreeNode(1); - const node2 = new RBTreeNode(2); - - node1.right = node2; - - expect(node1.right).toBe(node2); - expect(node2.parent).toBe(node1); - }); - - it('should set and get the parent correctly', () => { - const node1 = new RBTreeNode(1); - const node2 = new RBTreeNode(2); - - node1.left = node2; - - expect(node2.parent).toBe(node1); - expect(node1.left).toBe(node2); - }); - - it('should determine family position correctly', () => { - const root = new RBTreeNode(1); - const leftChild = new RBTreeNode(2); - const rightChild = new RBTreeNode(3); - - root.left = leftChild; - root.right = rightChild; - - expect(leftChild.familyPosition).toBe('LEFT'); - expect(rightChild.familyPosition).toBe('RIGHT'); - expect(root.familyPosition).toBe('ROOT'); - }); -}); - -describe('Red-Black Tree Tests', () => { - let tree: RBTree>; +describe('RedBlackTree', () => { + let tree: RedBlackTree; beforeEach(() => { - tree = new RBTree>(); + tree = new RedBlackTree(); }); - test('Insertion and In-order Traverse', () => { - tree.add(5); - // tree.add(3); - // tree.add(7); - // tree.add(2); - // tree.add(4); - // tree.add(6); - // tree.add(8); - // - // const inOrderTraverse: number[] = tree.DFS('in') - // - // expect(inOrderTraverse).toEqual([2, 3, 4, 5, 6, 7, 8]); + describe('insert and getNode', () => { + test('should insert and find a node in the tree', () => { + tree.insert(10); + tree.insert(20); + tree.insert(5); + + expect(tree.getNode(10)).toBeInstanceOf(RBTreeNode); + expect(tree.getNode(20)).toBeInstanceOf(RBTreeNode); + expect(tree.getNode(5)).toBeInstanceOf(RBTreeNode); + expect(tree.getNode(15)).toBe(tree.NIL); + }); + + test('should insert and find nodes with negative keys', () => { + tree.insert(-10); + tree.insert(-20); + + expect(tree.getNode(-10)).toBeInstanceOf(RBTreeNode); + expect(tree.getNode(-20)).toBeInstanceOf(RBTreeNode); + }); }); - test('Deletion', () => { - // tree.add(5); - // tree.add(3); - // tree.add(7); - // tree.add(2); - // tree.add(4); - // tree.add(6); - // tree.add(8); - // - // // Delete a node (e.g., 3) and check if it's gone - // tree.delete(3); - // expect(tree.has(3)).toBe(false); - // - // // Perform in-order traversal to check if the tree is still balanced - // const inOrderTraverse: number[] = tree.DFS('in'); - // - // - // expect(inOrderTraverse).toEqual([2, 4, 5, 6, 7, 8]); + describe('deleteNode', () => { + test('should delete a node from the tree', () => { + tree.insert(10); + tree.insert(20); + tree.insert(5); + tree.delete(20); + + expect(tree.getNode(20)).toBe(tree.NIL); + }); + + test('should handle deleting a non-existent node', () => { + tree.insert(10); + tree.insert(20); + tree.insert(5); + tree.delete(15); + + expect(tree.getNode(15)).toBe(tree.NIL); + }); + }); + + describe('minimum', () => { + test('should find the minimum node in the tree', () => { + tree.insert(10); + tree.insert(20); + tree.insert(5); + tree.insert(15); + tree.insert(3); + + const minNode = tree.getLeftMost(tree.root); + expect(minNode.key).toBe(3); + }); + + test('should handle an empty tree', () => { + const minNode = tree.getLeftMost(tree.root); + expect(minNode).toBe(tree.NIL); + }); + }); + + describe('getRightMost', () => { + test('should find the getRightMost node in the tree', () => { + tree.insert(10); + tree.insert(20); + tree.insert(5); + tree.insert(15); + tree.insert(25); + + const maxNode = tree.getRightMost(tree.root); + expect(maxNode.key).toBe(25); + }); + + test('should handle an empty tree', () => { + const maxNode = tree.getRightMost(tree.root); + expect(maxNode).toBe(tree.NIL); + }); + }); + + describe('getSuccessor', () => { + test('should find the getSuccessor of a node', () => { + tree.insert(10); + tree.insert(20); + tree.insert(5); + tree.insert(15); + tree.insert(25); + + const node = tree.getNode(15); + const successorNode = tree.getSuccessor(node); + + expect(successorNode.key).toBe(20); + }); + + test('should handle a node with no getSuccessor', () => { + tree.insert(10); + tree.insert(5); + + const node = tree.getNode(10); + const successorNode = tree.getSuccessor(node); + // TODO not sure if it should be null or tree.NIL + expect(successorNode).toBe(null); + }); + }); + + describe('getPredecessor', () => { + test('should find the getPredecessor of a node', () => { + tree.insert(10); + tree.insert(20); + tree.insert(5); + tree.insert(15); + tree.insert(25); + + const node = tree.getNode(20); + const predecessorNode = tree.getPredecessor(node); + + expect(predecessorNode.key).toBe(15); + }); + + test('should handle a node with no getPredecessor', () => { + tree.insert(10); + tree.insert(20); + + const node = tree.getNode(20); + const predecessorNode = tree.getPredecessor(node); + // TODO not sure if it should be tree.NIL or something else. + expect(predecessorNode).toBe(tree.getNode(10)); + }); }); });