diff --git a/src/data-structures/binary-tree/rb-tree.ts b/src/data-structures/binary-tree/rb-tree.ts index 7266828..7ec2be9 100644 --- a/src/data-structures/binary-tree/rb-tree.ts +++ b/src/data-structures/binary-tree/rb-tree.ts @@ -1,11 +1,11 @@ -import {BinaryTreeNodeKey, RBColor, RBTreeNodeNested, RBTreeOptions} from '../../types'; -import {IBinaryTree} from '../../interfaces'; +import {BinaryTreeDeletedResult, BinaryTreeNodeId, RBColor, RBTreeNodeNested, RBTreeOptions} from '../../types'; +import {IRBTree, IRBTreeNode} from '../../interfaces'; import {BST, BSTNode} from './bst'; -export class RBTreeNode = RBTreeNodeNested> extends BSTNode< - V, - FAMILY -> { +export class RBTreeNode = RBTreeNodeNested> + extends BSTNode + implements IRBTreeNode { + private _color: RBColor; constructor(key: BinaryTreeNodeKey, val?: V) { @@ -31,336 +31,295 @@ export class RBTree = RBTreeNode> extends BST< return new RBTreeNode(key, val) as N; } - // override add(keyOrNode: BinaryTreeNodeKey | N | null, val?: N['val']): N | null | undefined { - // const inserted = super.add(keyOrNode, val); - // if (inserted) this._fixInsertViolation(inserted); - // return inserted; - // } - // - // // Method for fixing insert violations in a red-black tree - // private _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 - // private _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 - // private _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; - // } - // - // private _isNodeRed(node: N | null | undefined): boolean { - // return node ? node.color === RBColor.RED : false; - // } - // - // // Find the sibling node - // private _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 - // private _removeNode(node: N, replacement: N | null | undefined): void { - // if (node === this.root && !replacement) { - // // If there's only the root node and no replacement, simply remove the root node - // this._setRoot(null); - // } else if (node === this.root || this._isNodeRed(node)) { - // // If the node is the root or a red node, remove 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 remove(nodeOrKey: BinaryTreeNodeKey | N): BinaryTreeDeletedResult[] { - // const node = this.get(nodeOrKey); - // 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 - // private _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; - // } - // } - // - // private _findMin(node: N): N { - // while (node.left) { - // node = node.left; - // } - // return node; - // } - // - // // Get the replacement node - // private _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 - // private _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; - // } + + private fixInsertion(node: N): void { + while (node !== this.root && node.parent?.color === RBColor.RED) { + if (node.parent === node.parent?.parent?.left) { + const uncle = node.parent?.parent?.right; + if (uncle?.color === RBColor.RED) { + node.parent.color = RBColor.BLACK; + uncle.color = RBColor.BLACK; + node.parent.parent.color = RBColor.RED; + node = node.parent.parent; + } else { + if (node === node.parent?.right) { + node = node.parent; + this.leftRotate(node); + } + if (node?.parent) node.parent.color = RBColor.BLACK; + if (node?.parent?.parent) { + node.parent.parent.color = RBColor.RED; + this.rightRotate(node.parent.parent); + } + } + } else { + const uncle = node.parent?.parent?.left; + if (uncle?.color === RBColor.RED) { + node.parent.color = RBColor.BLACK; + uncle.color = RBColor.BLACK; + if (node.parent.parent) { + node.parent.parent.color = RBColor.RED; + node = node.parent.parent; + } + } else { + if (node === node.parent?.left) { + node = node.parent; + this.rightRotate(node); + } + if (node.parent) node.parent.color = RBColor.BLACK; + if (node?.parent?.parent) { + node.parent.parent.color = RBColor.RED; + this.leftRotate(node.parent.parent); + } + } + } + } + if (this.root) this.root.color = RBColor.BLACK; + } + + private leftRotate(node: N): void { + const rightChild = node.right; + if (!rightChild) return; + + node.right = rightChild.left; + if (rightChild.left) { + rightChild.left.parent = node; + } + + rightChild.parent = node.parent; + if (!node.parent) { + this._setRoot(rightChild); + } else if (node === node.parent.left) { + node.parent.left = rightChild; + } else { + node.parent.right = rightChild; + } + + rightChild.left = node; + node.parent = rightChild; + // Update colors after rotation + const originalNodeColor = node.color; + node.color = rightChild.color; + rightChild.color = originalNodeColor; + } + + private rightRotate(node: N): void { + const leftChild = node.left; + if (!leftChild) return; + + node.left = leftChild.right; + if (leftChild.right) { + leftChild.right.parent = node; + } + + leftChild.parent = node.parent; + if (!node.parent) { + this._setRoot(leftChild); + } else if (node === node.parent.left) { + node.parent.left = leftChild; + } else { + node.parent.right = leftChild; + } + + leftChild.right = node; + node.parent = leftChild; + // Update colors after rotation + const originalNodeColor = node.color; + node.color = leftChild.color; + leftChild.color = originalNodeColor; + } + + override add(id: BinaryTreeNodeId, val?: N['val']) { + let newNode = this.createNode(id, val); + if (!this.root) { + // Set the root node color to BLACK if this is the first node + newNode.color = RBColor.BLACK; + this._setRoot(newNode); + } else { + this.insertNode(newNode); + this.fixInsertion(newNode); + // Update the root to the actual root of the tree after fixInsertion + while (newNode.parent) { + newNode = newNode.parent; + } + this._setRoot(newNode); + } + + return newNode; + } + + private insertNode(node: N): void { + let current: N | null | undefined = this.root; + let parent: N | null = null; + while (current) { + parent = current; + if (node.id < current.id) { + current = current.left; + } else { + current = current.right; + } + } + + node.parent = parent; + if (!parent) { + this._setRoot(node); + } else if (node.id < parent.id) { + parent.left = node; + } else { + parent.right = node; + } + } + + private transplant(u: N, v: N | null | undefined): void { + if (!u.parent) { + // If u is the root, set v as the new root + if (v !== undefined) this._setRoot(v); + } else if (u === u.parent.left) { + // If u is a left child, set v as the left child of u's parent + u.parent.left = v; + } else { + // If u is a right child, set v as the right child of u's parent + u.parent.right = v; + } + + if (v) { + // If v is not null, update its parent to be u's parent + v.parent = u.parent; + } + } + private minimum(node: N): N | null | undefined { + while (node.left) { + node = node.left; + } + return node; + } + + override remove(nodeOrId: BinaryTreeNodeId | N): BinaryTreeDeletedResult[] { + const deletedNodes: BinaryTreeDeletedResult[] = []; + + // Determine if the nodeOrId is a node or an ID + const node: N | null = typeof nodeOrId === 'number' ? this.get(nodeOrId) : nodeOrId; + if (!node) return deletedNodes; + + // We maintain a pointer to the deleted node and its parent. + const needBalanced: N | null = null; + + // Determine the color of the node to be deleted + let originalColor: RBColor = node.color; + + let child: N | null | undefined = null; + + if (!node.left) { + // Case 1: The node to be deleted has no left child + child = node.right; + this.transplant(node, node.right); + } else if (!node.right) { + // Case 2: The node to be deleted has no right child + child = node.left; + this.transplant(node, node.left); + } else { + // Case 3: The node to be deleted has two children + const successor = this.minimum(node.right); + if (successor) { + originalColor = successor?.color; + child = successor.right; + + if (successor.parent === node) { + if (child) child.parent = successor; // Update child's parent + } else { + this.transplant(successor, successor.right); + successor.right = node.right; + successor.right.parent = successor; + } + + this.transplant(node, successor); + successor.left = node.left; + successor.left.parent = successor; + successor.color = node.color; + } + + } + + if (originalColor === RBColor.BLACK) { + if (child) this.fixDeletion(child); + } + + // Update the tree size + this._setSize(this.size - 1); + + deletedNodes.push({ deleted: node, needBalanced }); + return deletedNodes; + } + private fixDeletion(node: N): void { + while (node !== this.root && node.color === RBColor.BLACK) { + if (node === node.parent?.left) { + let sibling = node.parent?.right; + if (sibling?.color === RBColor.RED) { + // Case 1: Sibling is red, perform rotation and recoloring. + sibling.color = RBColor.BLACK; + node.parent.color = RBColor.RED; + this.leftRotate(node.parent); + sibling = node.parent?.right; + } + if ( + (!sibling?.left || sibling.left.color === RBColor.BLACK) && + (!sibling?.right || sibling.right.color === RBColor.BLACK) + ) { + // Case 2: Both of sibling's children are black. + if (sibling) sibling.color = RBColor.RED; + node = node.parent; + } else { + if (!sibling?.right || sibling.right.color === RBColor.BLACK) { + // Case 3: Sibling's left child is red, right child is black. + sibling.left!.color = RBColor.BLACK; + sibling.color = RBColor.RED; + this.rightRotate(sibling); + sibling = node.parent?.right; + } + // Case 4: Sibling's right child is red. + sibling!.color = node.parent!.color; + node.parent!.color = RBColor.BLACK; + sibling!.right!.color = RBColor.BLACK; + this.leftRotate(node.parent!); + if (this.root) node = this.root; // Terminate the loop + } + } else { + let sibling = node.parent?.left; + if (sibling?.color === RBColor.RED) { + // Case 1: Sibling is red, perform rotation and recoloring. + sibling.color = RBColor.BLACK; + if (node.parent) { + node.parent.color = RBColor.RED; + this.rightRotate(node.parent); + sibling = node.parent?.left; + } + + } + if ( + (!sibling?.left || sibling.left.color === RBColor.BLACK) && + (!sibling?.right || sibling.right.color === RBColor.BLACK) + ) { + // Case 2: Both of sibling's children are black. + if (sibling) sibling.color = RBColor.RED; + if (node.parent) node = node.parent; + } else { + if (!sibling?.right || sibling.right.color === RBColor.BLACK) { + // Case 3: Sibling's right child is red, left child is black. + sibling.right!.color = RBColor.BLACK; + sibling.color = RBColor.RED; + this.leftRotate(sibling); + sibling = node.parent?.left; + } + // Case 4: Sibling's left child is red. + sibling!.color = node.parent!.color; + node.parent!.color = RBColor.BLACK; + sibling!.left!.color = RBColor.BLACK; + this.rightRotate(node.parent!); + if (this.root) node = this.root; // Terminate the loop + } + } + } + node.color = RBColor.BLACK; // Ensure the root is always black. + } + } diff --git a/test/integration/index.html b/test/integration/index.html index 5df808f..ab70fa2 100644 --- a/test/integration/index.html +++ b/test/integration/index.html @@ -40,8 +40,7 @@ console.log(performance.now() - startTime); - } - catch (e) { + } catch (e) { console.error(e); } diff --git a/test/unit/data-structures/binary-tree/avl-tree.test.ts b/test/unit/data-structures/binary-tree/avl-tree.test.ts index a5eb88c..acb6dce 100644 --- a/test/unit/data-structures/binary-tree/avl-tree.test.ts +++ b/test/unit/data-structures/binary-tree/avl-tree.test.ts @@ -1,6 +1,6 @@ import {AVLTree} from '../../../../src'; -describe('AVL Tree Test', () => { +describe('AVLTree operations', () => { it('should perform various operations on a AVL Tree', () => { const arr = [11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]; const tree = new AVLTree(); 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 55899bd..05f2381 100644 --- a/test/unit/data-structures/binary-tree/rb-tree.test.ts +++ b/test/unit/data-structures/binary-tree/rb-tree.test.ts @@ -1,43 +1,174 @@ -// import {RBTree, RBTreeNode} from '../../../../src'; +import { RBTree, RBTreeNode, RBColor } from '../../../../src'; + +// describe('Red-Black Tree Tests', () => { +// let tree: RBTree>; +// +// beforeEach(() => { +// tree = new RBTree>(); +// console.log('Initializing RBTree'); +// }); +// +// test('Insertion and In-order Traversal', () => { +// tree.add(5); +// tree.add(3); +// tree.add(7); +// tree.add(2); +// tree.add(4); +// tree.add(6); +// tree.add(8); +// +// const inOrderTraversal: number[] = tree.DFS('in'); +// console.log('In-order Traversal:', inOrderTraversal); +// expect(inOrderTraversal).toEqual([2, 3, 4, 5, 6, 7, 8]); +// }); +// +// 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.remove(3); +// console.log('Tree after deletion:', tree); +// expect(tree.has(3)).toBe(false); +// +// // Perform in-order traversal to check if the tree is still balanced +// const inOrderTraversal: number[] = tree.DFS('in'); +// expect(inOrderTraversal).toEqual([2, 4, 5, 6, 7, 8]); +// }); +// }); +// +// describe('RBTree', () => { +// let rbTree: RBTree>; +// +// beforeEach(() => { +// rbTree = new RBTree>(); +// }); +// +// it('should add and maintain Red-Black properties', () => { +// rbTree.add(10); +// rbTree.add(20); +// rbTree.add(30); +// rbTree.add(40); +// +// // Assert that the root node color is black +// expect(rbTree.root?.color).toBe(RBColor.BLACK); +// +// // Check the colors and parent-child relationships of other nodes +// const rootNode = rbTree.root; +// expect(rootNode?.id).toBe(20); +// expect(rootNode?.left?.id).toBe(10); +// expect(rootNode?.right?.id).toBe(30); +// expect(rootNode?.left?.color).toBe(RBColor.BLACK); +// expect(rootNode?.right?.color).toBe(RBColor.BLACK); +// +// const leftNode = rootNode?.left; +// const rightNode = rootNode?.right; +// expect(leftNode?.parent).toBe(rootNode); +// expect(rightNode?.parent).toBe(rootNode); +// }); +// +// it('should perform left rotation correctly', () => { +// rbTree.add(10); +// rbTree.add(20); +// rbTree.add(30); +// +// // Trigger left rotation +// rbTree.add(25); +// +// // Check the tree structure after left rotation +// const rootNode = rbTree.root; +// expect(rootNode?.id).toBe(20); +// +// const leftNode = rootNode?.left; +// const rightNode = rootNode?.right; +// expect(leftNode?.id).toBe(10); +// expect(rightNode?.id).toBe(25); +// +// expect(leftNode?.parent).toBe(rootNode); +// expect(rightNode?.parent).toBe(rootNode); +// }); +// +// it('should perform right rotation correctly', () => { +// rbTree.add(30); +// rbTree.add(20); +// rbTree.add(10); +// +// // Trigger right rotation +// rbTree.add(15); +// +// // Check the tree structure after right rotation +// const rootNode = rbTree.root; +// expect(rootNode?.id).toBe(20); +// +// const leftNode = rootNode?.left; +// const rightNode = rootNode?.right; +// expect(leftNode?.id).toBe(15); +// expect(rightNode?.id).toBe(30); +// +// expect(leftNode?.parent).toBe(rootNode); +// expect(rightNode?.parent).toBe(rootNode); +// }); +// }); + describe('Red-Black Tree Tests', () => { - // let tree: RBTree>; - // - // beforeEach(() => { - // tree = new RBTree>(); - // }); + let tree: RBTree>; - test('Insertion and In-order Traversal', () => { - // tree.add(5); - // tree.add(3); - // tree.add(7); - // tree.add(2); - // tree.add(4); - // tree.add(6); - // tree.add(8); - // - // const inOrderTraversal: number[] = tree.DFS('in') - // - // expect(inOrderTraversal).toEqual([2, 3, 4, 5, 6, 7, 8]); + beforeEach(() => { + tree = new RBTree>(); }); - 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.remove(3); - // expect(tree.has(3)).toBe(false); - // - // // Perform in-order traversal to check if the tree is still balanced - // const inOrderTraversal: number[] = tree.DFS('in'); - // - // - // expect(inOrderTraversal).toEqual([2, 4, 5, 6, 7, 8]); + it('Insertion and Red-Black Properties', () => { + tree.add(10); + tree.add(20); + tree.add(30); + tree.add(25); + + // Assert the root node color is black + expect(tree.root?.color).toBe(RBColor.BLACK); + + // Check the colors and parent-child relationships of other nodes + const rootNode = tree.root; + expect(rootNode?.id).toBe(25); + expect(rootNode?.left?.id).toBe(10); + expect(rootNode?.right?.id).toBe(30); + expect(rootNode?.left?.left?.id).toBe(20); + + // Check colors + expect(rootNode?.color).toBe(RBColor.BLACK); + expect(rootNode?.left?.color).toBe(RBColor.BLACK); + expect(rootNode?.right?.color).toBe(RBColor.BLACK); + expect(rootNode?.left?.left?.color).toBe(RBColor.RED); + }); + + it('Deletion and Red-Black Properties', () => { + tree.add(10); + tree.add(20); + tree.add(30); + tree.add(25); + + // Delete a node (e.g., 20) and check if it's gone + tree.remove(20); + expect(tree.has(20)).toBe(false); + + // Perform in-order traversal to check if the tree is still balanced + const inOrderTraversal: number[] = tree.DFS('in'); + expect(inOrderTraversal).toEqual([10, 25, 30]); + + // Check colors + expect(tree.root?.color).toBe(RBColor.BLACK); + expect(tree.root?.left?.color).toBe(RBColor.BLACK); + expect(tree.root?.right?.color).toBe(RBColor.BLACK); + + // Set the root node color to black (add this line to fix the issue) + if (tree.root) tree.root.color = RBColor.BLACK; }); }); + + +