From 19221d698f0e6f19900174507479bdcc96692628 Mon Sep 17 00:00:00 2001 From: Revone Date: Thu, 19 Oct 2023 20:12:45 +0800 Subject: [PATCH 1/2] [rbtree] almost there, RBTree has been implemented, but with bugs in deletion --- src/data-structures/binary-tree/bst.ts | 2 +- src/data-structures/binary-tree/rb-tree.ts | 643 +++++++++--------- src/types/data-structures/binary-tree.ts | 2 +- .../binary-tree/rb-tree.test.ts | 320 ++++++++- 4 files changed, 598 insertions(+), 369 deletions(-) diff --git a/src/data-structures/binary-tree/bst.ts b/src/data-structures/binary-tree/bst.ts index 22b1af6..1c0a51d 100644 --- a/src/data-structures/binary-tree/bst.ts +++ b/src/data-structures/binary-tree/bst.ts @@ -214,7 +214,7 @@ export class BST = BSTNode> extends BinaryTree * @returns The method is returning either a BinaryTreeNodeKey or N (generic type) or null. */ override get(nodeProperty: BinaryTreeNodeKey | N, propertyName: BinaryTreeNodePropertyName = 'key'): N | null { - return this.getNodes(nodeProperty, propertyName, true)[0] ?? null; + return this.getNodes(nodeProperty, propertyName, true)[0] ?? undefined; } /** diff --git a/src/data-structures/binary-tree/rb-tree.ts b/src/data-structures/binary-tree/rb-tree.ts index 7266828..3ba36c6 100644 --- a/src/data-structures/binary-tree/rb-tree.ts +++ b/src/data-structures/binary-tree/rb-tree.ts @@ -1,4 +1,4 @@ -import {BinaryTreeNodeKey, RBColor, RBTreeNodeNested, RBTreeOptions} from '../../types'; +import {BinaryTreeDeletedResult, BinaryTreeNodeKey, RBColor, RBTreeNodeNested, RBTreeOptions} from '../../types'; import {IBinaryTree} from '../../interfaces'; import {BST, BSTNode} from './bst'; @@ -31,336 +31,313 @@ 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; - // } + override add(key: BinaryTreeNodeKey, val?: N['val']): N { + const newNode = this.createNode(key, val); + if (!this.root) { + this._setRoot(newNode); + newNode.color = RBColor.BLACK; + } else { + this.insertNode(this.root, newNode); + this.fixInsertViolations(newNode); + } + this._setSize(this.size + 1); + return newNode; + } + + private insertNode(root: N, node: N): void { + if (node.key < root.key) { + if (!root.left) { + root.left = node; + node.parent = root; + } else { + this.insertNode(root.left as N, node); + } + } else if (node.key > root.key) { + if (!root.right) { + root.right = node; + node.parent = root; + } else { + this.insertNode(root.right as N, node); + } + } + } + + private fixInsertViolations(node: N): void { + let parent: N | undefined = undefined; + let grandparent: N | undefined = undefined; + + while (node !== this.root && node.color === RBColor.RED && node.parent?.color === RBColor.RED) { + parent = node.parent as N; + grandparent = parent.parent as N; + + if (parent === grandparent.left) { + const uncle = grandparent.right; + + if (uncle && uncle.color === RBColor.RED) { + grandparent.color = RBColor.RED; + parent.color = RBColor.BLACK; + uncle.color = RBColor.BLACK; + node = grandparent; + } else { + if (node === parent.right) { + this.rotateLeft(parent); + node = parent; + parent = node.parent as N; + } + + this.rotateRight(grandparent as N); + + const tempColor = parent.color; + parent.color = grandparent.color; + grandparent.color = tempColor; + + node = parent; + } + } else { + const uncle = grandparent.left; + + if (uncle && uncle.color === RBColor.RED) { + grandparent.color = RBColor.RED; + parent.color = RBColor.BLACK; + uncle.color = RBColor.BLACK; + node = grandparent; + } else { + if (node === parent.left) { + this.rotateRight(parent); + node = parent; + parent = node.parent as N; + } + + this.rotateLeft(grandparent as N); + + const tempColor = parent.color; + parent.color = grandparent.color; + grandparent.color = tempColor; + + node = parent; + } + } + } + + if (this.root) this.root.color = RBColor.BLACK; + } + + private rotateLeft(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; + } + + private rotateRight(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.right) { + node.parent.right = leftChild; + } else { + node.parent.left = leftChild; + } + leftChild.right = node; + node.parent = leftChild; + } + + remove(nodeOrKey: BinaryTreeNodeKey | N): BinaryTreeDeletedResult[] { + const result: BinaryTreeDeletedResult[] = []; + if (this.root) { + const nodeToRemove = typeof nodeOrKey === 'number' ? this.get(nodeOrKey) : nodeOrKey; + if (nodeToRemove) { + this.deleteNode(nodeToRemove); + this._setSize(this.size - 1); + result.push({deleted: nodeToRemove, needBalanced: undefined}); + } + } + return result; + } + + private deleteNode(node: N): void { + const parent = node.parent as N; + const isLeftChild = parent && node === parent.left; + + if (!node.left && !node.right) { + if (!parent) { + this._setRoot(null); + } else { + if (!isLeftChild) { + parent.right = undefined; + } else { + parent.left = undefined; + } + } + return; + } + + const child = node.left || node.right; + if (child) child.parent = parent; + + if (!parent) { + if (child) { + this._setRoot(child); + if (node.color === RBColor.BLACK && child.color === RBColor.RED) { + child.color = RBColor.BLACK; + } + } + } else { + if (!isLeftChild) { + parent.right = child; + } else { + parent.left = child; + } + + if (node.color === RBColor.BLACK) { + if (child && child.color === RBColor.RED) { + // 如果删除的节点是黑色,而子节点是红色,直接将子节点染成黑色 + child.color = RBColor.BLACK; + } else { + // 否则,进入修复违反红黑树性质的情况 + this.fixDeleteViolations(child, parent, isLeftChild); + } + } + + } + } + + private fixDeleteViolations(node: N | null | undefined, parent: N | undefined, isLeftChild: boolean): void { + let sibling: N | undefined; + + while (node !== this.root && (!node || node.color === RBColor.BLACK)) { + if (isLeftChild) { + sibling = parent?.right as N; + + if (sibling?.color === RBColor.RED) { + // Case 1: Sibling is red, transform into a case where sibling is black + sibling.color = RBColor.BLACK; + parent!.color = RBColor.RED; + this.rotateLeft(parent!); + sibling = parent!.right as N; + } + + if ( + (!sibling?.left || sibling.left.color === RBColor.BLACK) && + (!sibling?.right || sibling.right.color === RBColor.BLACK) + ) { + // Case 2: Sibling and both of its children are black + sibling.color = RBColor.RED; + node = parent; + parent = node?.parent as N; + isLeftChild = node === parent?.left; + } else { + if (!sibling) { + break; + } + + if (!sibling.right || sibling.right.color === RBColor.BLACK) { + // Case 3: Sibling is black, sibling's left child is red, and sibling's right child is black + sibling!.left!.color = RBColor.BLACK; + sibling.color = RBColor.RED; + this.rotateRight(sibling); + sibling = parent?.right as N; + } + + // Case 4: Sibling is black, sibling's right child is red + sibling.color = parent?.color as RBColor; + parent!.color = RBColor.BLACK; + sibling.right!.color = RBColor.BLACK; + this.rotateLeft(parent!); + node = this.root; + } + } else { + sibling = parent?.left as N; + + if (sibling?.color === RBColor.RED) { + // Case 1: Sibling is red, transform into a case where sibling is black + sibling.color = RBColor.BLACK; + parent!.color = RBColor.RED; + this.rotateRight(parent!); + sibling = parent!.left as N; + } + + if ( + (!sibling?.right || sibling.right.color === RBColor.BLACK) && + (!sibling?.left || sibling.left.color === RBColor.BLACK) + ) { + // Case 2: Sibling and both of its children are black + sibling.color = RBColor.RED; + node = parent; + parent = node?.parent as N; + isLeftChild = node === parent?.left; + } else { + if (!sibling) { + break; + } + + if (!sibling.left || sibling.left.color === RBColor.BLACK) { + // Case 3: Sibling is black, sibling's right child is red, and sibling's left child is black + sibling!.right!.color = RBColor.BLACK; + sibling.color = RBColor.RED; + this.rotateLeft(sibling); + sibling = parent!.left as N; + } + + // Case 4: Sibling is black, sibling's left child is red + sibling.color = parent?.color as RBColor; + parent!.color = RBColor.BLACK; + sibling.left!.color = RBColor.BLACK; + this.rotateRight(parent!); + node = this.root; + } + } + } + + if (node) { + node.color = RBColor.BLACK; + } + } + + + isRBTree(): boolean { + + if (this.root) { + const rootBlackHeight = this.computeBlackHeight(this.root); + return rootBlackHeight > 0; + } + + return true; + } + + private computeBlackHeight(node: N | null | undefined): number { + if (!node) { + + return 1; + } + + const leftHeight = this.computeBlackHeight(node.left); + const rightHeight = this.computeBlackHeight(node.right); + + if (leftHeight > 0 && rightHeight > 0 && leftHeight === rightHeight) { + return node.color === RBColor.BLACK ? leftHeight + 1 : leftHeight; + } else { + return 0; + } + } } diff --git a/src/types/data-structures/binary-tree.ts b/src/types/data-structures/binary-tree.ts index a5f8f04..d4fe61b 100644 --- a/src/types/data-structures/binary-tree.ts +++ b/src/types/data-structures/binary-tree.ts @@ -35,7 +35,7 @@ export type BinaryTreeNodeProperty> = | N | number | BinaryTreeNodeKey; -export type BinaryTreeDeletedResult = { deleted: N | null | undefined; needBalanced: N | null }; +export type BinaryTreeDeletedResult = { deleted: N | null | undefined; needBalanced: N | null | undefined }; export type BinaryTreeNodeProperties> = BinaryTreeNodeProperty[]; 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..3157be0 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,295 @@ -// import {RBTree, RBTreeNode} from '../../../../src'; +import {RBTree, RBTreeNode} from '../../../../src'; describe('Red-Black Tree Tests', () => { - // let tree: RBTree>; - // - // beforeEach(() => { - // tree = new RBTree>(); - // }); + let tree: RBTree>; + + beforeEach(() => { + tree = new 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]); + 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]); }); 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]); + 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'); + console.log(inOrderTraversal) + + + expect(inOrderTraversal).toEqual([2, 4, 5, 6, 7, 8]); }); }); + +describe('Red-Black Tree', () => { + let tree: RBTree>; + + beforeEach(() => { + tree = new RBTree>(); + }); + + it('should insert and retrieve values correctly', () => { + tree.add(10); + tree.add(20); + tree.add(5); + tree.add(15); + + expect(tree.get(10)).toBeDefined(); + expect(tree.get(20)).toBeDefined(); + expect(tree.get(5)).toBeDefined(); + expect(tree.get(15)).toBeDefined(); + expect(tree.get(30)).toBeUndefined(); + }); + + it('should remove values correctly', () => { + tree.add(10); + tree.add(20); + tree.add(5); + tree.add(15); + tree.add(30); + tree.remove(20); + + expect(tree.get(20)).toBeUndefined(); + }); + + it('should perform in-order traversal', () => { + tree.add(10); + tree.add(20); + tree.add(5); + tree.add(15); + tree.add(30); + + const result: number[] = tree.dfs('in'); + + expect(result).toEqual([5, 10, 15, 20, 30]); + }); + + it('should validate red-black tree properties', () => { + tree.add(10); + tree.add(20); + tree.add(5); + tree.add(15); + + // expect(tree.isRBTree()).toBeTruthy(); + }); +}); + +describe('RBTree', () => { + let tree: RBTree; + + beforeEach(() => { + tree = new RBTree(); + }); + + test('should insert and find nodes correctly', () => { + tree.add(5); + tree.add(3); + tree.add(8); + + expect(tree.get(5)?.key).toBe(5); + expect(tree.get(3)?.key).toBe(3); + expect(tree.get(8)?.key).toBe(8); + }); + + test('should remove nodes correctly', () => { + tree.add(5); + tree.add(3); + tree.add(8); + + tree.remove(3); + expect(tree.get(3)).toBe(undefined); + + tree.remove(5); + expect(tree.get(5)).toBe(undefined); + + tree.remove(8); + expect(tree.get(8)).toBe(undefined); + }); + + test('should validate red-black tree properties', () => { + tree.add(5); + tree.add(3); + tree.add(8); + + expect(tree.isRBTree()).toBe(true); + expect(tree.isAVLBalanced()).toBe(true); + expect(tree.isPerfectlyBalanced()).toBe(true); + expect(tree.isEmpty()).toBe(false); + tree.remove(8); + expect(tree.size).toBe(2); + tree.remove(9); + expect(tree.size).toBe(2); + tree.remove(5); + expect(tree.size).toBe(1); + tree.remove(3); + expect(tree.size).toBe(0); + }); + + test('should handle various insert and remove scenarios', () => { + // Test your tree with various insertion and deletion scenarios + // and validate that it maintains red-black tree properties. + // Make sure to test both simple and complex cases. + + // For example: + tree.add(5); + tree.add(3); + tree.add(8); + tree.remove(3); + expect(tree.isRBTree()).toBe(true); + + // Add more test scenarios here. + }); +}); + +describe('RBTree', () => { + let tree: RBTree; + + beforeEach(() => { + tree = new RBTree(); + }); + + it('should add nodes and maintain RBTree properties', () => { + tree.add(10); + tree.add(5); + tree.add(15); + tree.add(3); + tree.add(7); + + // Make assertions here to check that the RBTree properties are maintained + // For example, check that there are no consecutive red nodes + }); + + it('should remove nodes and maintain RBTree properties', () => { + tree.add(10); + tree.add(5); + tree.add(15); + tree.add(3); + tree.add(7); + + tree.remove(5); + + // Make assertions here to check that the RBTree properties are maintained + }); + + it('should find nodes by key', () => { + tree.add(10, 'A'); + tree.add(5, 'B'); + tree.add(15, 'C'); + tree.add(3, 'D'); + tree.add(7, 'E'); + + const node = tree.get(5); + + expect(node?.val).toBe('B'); + }); + + it('should check RBTree properties', () => { + tree.add(10); + tree.add(5); + tree.add(15); + tree.add(3); + tree.add(7); + + const isRBTree = tree.isRBTree(); + + expect(isRBTree).toBe(true); + }); +}); + + + +describe('RBTree', () => { + it('should insert and find elements correctly', () => { + const tree = new RBTree>(); + + + tree.add(10); + tree.add(20); + tree.add(5); + + expect(tree.size).toBe(3); + expect(tree.get(10)?.key).toBe(10); + expect(tree.get(20)?.key).toBe(20); + expect(tree.get(5)?.key).toBe(5); + }); + + it('should delete elements correctly', () => { + const tree = new RBTree>(); + + + tree.add(10); + tree.add(20); + tree.add(5); + + tree.remove(10); + expect(tree.size).toBe(2); + expect(tree.get(10)).toBeUndefined(); + }); + + it('should balance the tree after insertions', () => { + const tree = new RBTree>(); + + + tree.add(10); + tree.add(20); + tree.add(5); + + // You can add expectations to check the tree's structure after insertions + // For example, checking that the root is black, and the leaves are black, etc. + }); + + it('should maintain Red-Black properties after deletions', () => { + const tree = new RBTree>(); + + tree.add(10); + tree.add(20); + tree.add(5); + + tree.remove(20); + + // You can add expectations to check Red-Black properties + // For example, checking that there are no two consecutive red nodes in a path, etc. + expect(tree.isRBTree()).toBe(true); + }); + + it('should handle edge cases', () => { + const tree = new RBTree>(); + + + // Test with an empty tree + expect(tree.size).toBe(0); + expect(tree.isRBTree()).toBe(true); + + // Test deleting non-existent elements + tree.add(10); + tree.remove(20); + expect(tree.size).toBe(1); + + // Test deleting the root element + tree.remove(10); + expect(tree.size).toBe(0); + }); +}); + From 7b0dddc69e6b0a6ed6efc4a7ea327de8c7aa1b7b Mon Sep 17 00:00:00 2001 From: Revone Date: Tue, 31 Oct 2023 11:38:54 +0800 Subject: [PATCH 2/2] [rbtree branch] conflicts resolved --- src/data-structures/binary-tree/bst.ts | 5 - src/data-structures/binary-tree/rb-tree.ts | 316 ------------------ .../binary-tree/binary-tree.ts | 15 - .../binary-tree/rb-tree.test.ts | 295 ---------------- 4 files changed, 631 deletions(-) diff --git a/src/data-structures/binary-tree/bst.ts b/src/data-structures/binary-tree/bst.ts index 94cd873..e033122 100644 --- a/src/data-structures/binary-tree/bst.ts +++ b/src/data-structures/binary-tree/bst.ts @@ -231,10 +231,6 @@ export class BST = BSTNode> * @returns either the first node that matches the given nodeProperty and callback, or null if no * matching node is found. */ -<<<<<<< HEAD - override get(nodeProperty: BinaryTreeNodeKey | N, propertyName: BinaryTreeNodePropertyName = 'key'): N | null { - return this.getNodes(nodeProperty, propertyName, true)[0] ?? undefined; -======= override get>( identifier: ReturnType | null, callback: C = ((node: N) => node.key) as C, @@ -242,7 +238,6 @@ export class BST = BSTNode> iterationType = this.iterationType ): N | null { return this.getNodes(identifier, callback, true, beginRoot, iterationType)[0] ?? null; ->>>>>>> 10bbcffcef4ed5901867431a3d3eae891d190b9d } /** diff --git a/src/data-structures/binary-tree/rb-tree.ts b/src/data-structures/binary-tree/rb-tree.ts index 7d4c6c1..3018dc7 100644 --- a/src/data-structures/binary-tree/rb-tree.ts +++ b/src/data-structures/binary-tree/rb-tree.ts @@ -1,8 +1,4 @@ -<<<<<<< HEAD -import {BinaryTreeDeletedResult, BinaryTreeNodeKey, RBColor, RBTreeNodeNested, RBTreeOptions} from '../../types'; -======= import {BTNKey, RBColor, RBTreeNodeNested, RBTreeOptions} from '../../types'; ->>>>>>> 10bbcffcef4ed5901867431a3d3eae891d190b9d import {IBinaryTree} from '../../interfaces'; import {BST, BSTNode} from './bst'; @@ -27,317 +23,6 @@ export class RBTree = RBTreeNode root.key) { - if (!root.right) { - root.right = node; - node.parent = root; - } else { - this.insertNode(root.right as N, node); - } - } - } - - private fixInsertViolations(node: N): void { - let parent: N | undefined = undefined; - let grandparent: N | undefined = undefined; - - while (node !== this.root && node.color === RBColor.RED && node.parent?.color === RBColor.RED) { - parent = node.parent as N; - grandparent = parent.parent as N; - - if (parent === grandparent.left) { - const uncle = grandparent.right; - - if (uncle && uncle.color === RBColor.RED) { - grandparent.color = RBColor.RED; - parent.color = RBColor.BLACK; - uncle.color = RBColor.BLACK; - node = grandparent; - } else { - if (node === parent.right) { - this.rotateLeft(parent); - node = parent; - parent = node.parent as N; - } - - this.rotateRight(grandparent as N); - - const tempColor = parent.color; - parent.color = grandparent.color; - grandparent.color = tempColor; - - node = parent; - } - } else { - const uncle = grandparent.left; - - if (uncle && uncle.color === RBColor.RED) { - grandparent.color = RBColor.RED; - parent.color = RBColor.BLACK; - uncle.color = RBColor.BLACK; - node = grandparent; - } else { - if (node === parent.left) { - this.rotateRight(parent); - node = parent; - parent = node.parent as N; - } - - this.rotateLeft(grandparent as N); - - const tempColor = parent.color; - parent.color = grandparent.color; - grandparent.color = tempColor; - - node = parent; - } - } - } - - if (this.root) this.root.color = RBColor.BLACK; - } - - private rotateLeft(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; - } - - private rotateRight(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.right) { - node.parent.right = leftChild; - } else { - node.parent.left = leftChild; - } - leftChild.right = node; - node.parent = leftChild; - } - - remove(nodeOrKey: BinaryTreeNodeKey | N): BinaryTreeDeletedResult[] { - const result: BinaryTreeDeletedResult[] = []; - if (this.root) { - const nodeToRemove = typeof nodeOrKey === 'number' ? this.get(nodeOrKey) : nodeOrKey; - if (nodeToRemove) { - this.deleteNode(nodeToRemove); - this._setSize(this.size - 1); - result.push({deleted: nodeToRemove, needBalanced: undefined}); - } - } - return result; - } - - private deleteNode(node: N): void { - const parent = node.parent as N; - const isLeftChild = parent && node === parent.left; - - if (!node.left && !node.right) { - if (!parent) { - this._setRoot(null); - } else { - if (!isLeftChild) { - parent.right = undefined; - } else { - parent.left = undefined; - } - } - return; - } - - const child = node.left || node.right; - if (child) child.parent = parent; - - if (!parent) { - if (child) { - this._setRoot(child); - if (node.color === RBColor.BLACK && child.color === RBColor.RED) { - child.color = RBColor.BLACK; - } - } - } else { - if (!isLeftChild) { - parent.right = child; - } else { - parent.left = child; - } - - if (node.color === RBColor.BLACK) { - if (child && child.color === RBColor.RED) { - // 如果删除的节点是黑色,而子节点是红色,直接将子节点染成黑色 - child.color = RBColor.BLACK; - } else { - // 否则,进入修复违反红黑树性质的情况 - this.fixDeleteViolations(child, parent, isLeftChild); - } - } - - } - } - - private fixDeleteViolations(node: N | null | undefined, parent: N | undefined, isLeftChild: boolean): void { - let sibling: N | undefined; - - while (node !== this.root && (!node || node.color === RBColor.BLACK)) { - if (isLeftChild) { - sibling = parent?.right as N; - - if (sibling?.color === RBColor.RED) { - // Case 1: Sibling is red, transform into a case where sibling is black - sibling.color = RBColor.BLACK; - parent!.color = RBColor.RED; - this.rotateLeft(parent!); - sibling = parent!.right as N; - } - - if ( - (!sibling?.left || sibling.left.color === RBColor.BLACK) && - (!sibling?.right || sibling.right.color === RBColor.BLACK) - ) { - // Case 2: Sibling and both of its children are black - sibling.color = RBColor.RED; - node = parent; - parent = node?.parent as N; - isLeftChild = node === parent?.left; - } else { - if (!sibling) { - break; - } - - if (!sibling.right || sibling.right.color === RBColor.BLACK) { - // Case 3: Sibling is black, sibling's left child is red, and sibling's right child is black - sibling!.left!.color = RBColor.BLACK; - sibling.color = RBColor.RED; - this.rotateRight(sibling); - sibling = parent?.right as N; - } - - // Case 4: Sibling is black, sibling's right child is red - sibling.color = parent?.color as RBColor; - parent!.color = RBColor.BLACK; - sibling.right!.color = RBColor.BLACK; - this.rotateLeft(parent!); - node = this.root; - } - } else { - sibling = parent?.left as N; - - if (sibling?.color === RBColor.RED) { - // Case 1: Sibling is red, transform into a case where sibling is black - sibling.color = RBColor.BLACK; - parent!.color = RBColor.RED; - this.rotateRight(parent!); - sibling = parent!.left as N; - } - - if ( - (!sibling?.right || sibling.right.color === RBColor.BLACK) && - (!sibling?.left || sibling.left.color === RBColor.BLACK) - ) { - // Case 2: Sibling and both of its children are black - sibling.color = RBColor.RED; - node = parent; - parent = node?.parent as N; - isLeftChild = node === parent?.left; - } else { - if (!sibling) { - break; - } - - if (!sibling.left || sibling.left.color === RBColor.BLACK) { - // Case 3: Sibling is black, sibling's right child is red, and sibling's left child is black - sibling!.right!.color = RBColor.BLACK; - sibling.color = RBColor.RED; - this.rotateLeft(sibling); - sibling = parent!.left as N; - } - - // Case 4: Sibling is black, sibling's left child is red - sibling.color = parent?.color as RBColor; - parent!.color = RBColor.BLACK; - sibling.left!.color = RBColor.BLACK; - this.rotateRight(parent!); - node = this.root; - } - } - } - - if (node) { - node.color = RBColor.BLACK; - } - } - - - isRBTree(): boolean { - - if (this.root) { - const rootBlackHeight = this.computeBlackHeight(this.root); - return rootBlackHeight > 0; - } - - return true; - } - - private computeBlackHeight(node: N | null | undefined): number { - if (!node) { - - return 1; - } - - const leftHeight = this.computeBlackHeight(node.left); - const rightHeight = this.computeBlackHeight(node.right); - - if (leftHeight > 0 && rightHeight > 0 && leftHeight === rightHeight) { - return node.color === RBColor.BLACK ? leftHeight + 1 : leftHeight; - } else { - return 0; - } - } -======= // override add(keyOrNode: BTNKey | N | null, value?: V): N | null | undefined { // const inserted = super.add(keyOrNode, value); // if (inserted) this._fixInsertViolation(inserted); @@ -670,5 +355,4 @@ export class RBTree = RBTreeNode>>>>>> 10bbcffcef4ed5901867431a3d3eae891d190b9d } diff --git a/src/types/data-structures/binary-tree/binary-tree.ts b/src/types/data-structures/binary-tree/binary-tree.ts index b4afc49..c9da335 100644 --- a/src/types/data-structures/binary-tree/binary-tree.ts +++ b/src/types/data-structures/binary-tree/binary-tree.ts @@ -24,22 +24,7 @@ export enum FamilyPosition { export type BTNKey = number; -<<<<<<< HEAD:src/types/data-structures/binary-tree.ts -export type NodeOrPropertyName = 'node' | BinaryTreeNodePropertyName; - -export type DFSOrderPattern = 'in' | 'pre' | 'post'; - -export type BinaryTreeNodeKey = number; - -export type BinaryTreeNodeProperty> = - | N['val'] - | N - | number - | BinaryTreeNodeKey; -export type BinaryTreeDeletedResult = { deleted: N | null | undefined; needBalanced: N | null | undefined }; -======= export type BinaryTreeDeletedResult = { deleted: N | null | undefined; needBalanced: N | null }; ->>>>>>> 10bbcffcef4ed5901867431a3d3eae891d190b9d:src/types/data-structures/binary-tree/binary-tree.ts export type BinaryTreeNodeNested = BinaryTreeNode>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 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 b159b3d..43d817f 100644 --- a/test/unit/data-structures/binary-tree/rb-tree.test.ts +++ b/test/unit/data-structures/binary-tree/rb-tree.test.ts @@ -1,47 +1,4 @@ import {RBTree, RBTreeNode} from '../../../../src'; -<<<<<<< HEAD - -describe('Red-Black Tree Tests', () => { - let tree: RBTree>; - - beforeEach(() => { - tree = new 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]); - }); - - 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'); - console.log(inOrderTraversal) - - - expect(inOrderTraversal).toEqual([2, 4, 5, 6, 7, 8]); -======= describe('RBTreeNode', () => { it('should create an instance of RBTreeNode', () => { @@ -148,257 +105,5 @@ describe('Red-Black Tree Tests', () => { // // // expect(inOrderTraverse).toEqual([2, 4, 5, 6, 7, 8]); ->>>>>>> 10bbcffcef4ed5901867431a3d3eae891d190b9d }); }); - -describe('Red-Black Tree', () => { - let tree: RBTree>; - - beforeEach(() => { - tree = new RBTree>(); - }); - - it('should insert and retrieve values correctly', () => { - tree.add(10); - tree.add(20); - tree.add(5); - tree.add(15); - - expect(tree.get(10)).toBeDefined(); - expect(tree.get(20)).toBeDefined(); - expect(tree.get(5)).toBeDefined(); - expect(tree.get(15)).toBeDefined(); - expect(tree.get(30)).toBeUndefined(); - }); - - it('should remove values correctly', () => { - tree.add(10); - tree.add(20); - tree.add(5); - tree.add(15); - tree.add(30); - tree.remove(20); - - expect(tree.get(20)).toBeUndefined(); - }); - - it('should perform in-order traversal', () => { - tree.add(10); - tree.add(20); - tree.add(5); - tree.add(15); - tree.add(30); - - const result: number[] = tree.dfs('in'); - - expect(result).toEqual([5, 10, 15, 20, 30]); - }); - - it('should validate red-black tree properties', () => { - tree.add(10); - tree.add(20); - tree.add(5); - tree.add(15); - - // expect(tree.isRBTree()).toBeTruthy(); - }); -}); - -describe('RBTree', () => { - let tree: RBTree; - - beforeEach(() => { - tree = new RBTree(); - }); - - test('should insert and find nodes correctly', () => { - tree.add(5); - tree.add(3); - tree.add(8); - - expect(tree.get(5)?.key).toBe(5); - expect(tree.get(3)?.key).toBe(3); - expect(tree.get(8)?.key).toBe(8); - }); - - test('should remove nodes correctly', () => { - tree.add(5); - tree.add(3); - tree.add(8); - - tree.remove(3); - expect(tree.get(3)).toBe(undefined); - - tree.remove(5); - expect(tree.get(5)).toBe(undefined); - - tree.remove(8); - expect(tree.get(8)).toBe(undefined); - }); - - test('should validate red-black tree properties', () => { - tree.add(5); - tree.add(3); - tree.add(8); - - expect(tree.isRBTree()).toBe(true); - expect(tree.isAVLBalanced()).toBe(true); - expect(tree.isPerfectlyBalanced()).toBe(true); - expect(tree.isEmpty()).toBe(false); - tree.remove(8); - expect(tree.size).toBe(2); - tree.remove(9); - expect(tree.size).toBe(2); - tree.remove(5); - expect(tree.size).toBe(1); - tree.remove(3); - expect(tree.size).toBe(0); - }); - - test('should handle various insert and remove scenarios', () => { - // Test your tree with various insertion and deletion scenarios - // and validate that it maintains red-black tree properties. - // Make sure to test both simple and complex cases. - - // For example: - tree.add(5); - tree.add(3); - tree.add(8); - tree.remove(3); - expect(tree.isRBTree()).toBe(true); - - // Add more test scenarios here. - }); -}); - -describe('RBTree', () => { - let tree: RBTree; - - beforeEach(() => { - tree = new RBTree(); - }); - - it('should add nodes and maintain RBTree properties', () => { - tree.add(10); - tree.add(5); - tree.add(15); - tree.add(3); - tree.add(7); - - // Make assertions here to check that the RBTree properties are maintained - // For example, check that there are no consecutive red nodes - }); - - it('should remove nodes and maintain RBTree properties', () => { - tree.add(10); - tree.add(5); - tree.add(15); - tree.add(3); - tree.add(7); - - tree.remove(5); - - // Make assertions here to check that the RBTree properties are maintained - }); - - it('should find nodes by key', () => { - tree.add(10, 'A'); - tree.add(5, 'B'); - tree.add(15, 'C'); - tree.add(3, 'D'); - tree.add(7, 'E'); - - const node = tree.get(5); - - expect(node?.val).toBe('B'); - }); - - it('should check RBTree properties', () => { - tree.add(10); - tree.add(5); - tree.add(15); - tree.add(3); - tree.add(7); - - const isRBTree = tree.isRBTree(); - - expect(isRBTree).toBe(true); - }); -}); - - - -describe('RBTree', () => { - it('should insert and find elements correctly', () => { - const tree = new RBTree>(); - - - tree.add(10); - tree.add(20); - tree.add(5); - - expect(tree.size).toBe(3); - expect(tree.get(10)?.key).toBe(10); - expect(tree.get(20)?.key).toBe(20); - expect(tree.get(5)?.key).toBe(5); - }); - - it('should delete elements correctly', () => { - const tree = new RBTree>(); - - - tree.add(10); - tree.add(20); - tree.add(5); - - tree.remove(10); - expect(tree.size).toBe(2); - expect(tree.get(10)).toBeUndefined(); - }); - - it('should balance the tree after insertions', () => { - const tree = new RBTree>(); - - - tree.add(10); - tree.add(20); - tree.add(5); - - // You can add expectations to check the tree's structure after insertions - // For example, checking that the root is black, and the leaves are black, etc. - }); - - it('should maintain Red-Black properties after deletions', () => { - const tree = new RBTree>(); - - tree.add(10); - tree.add(20); - tree.add(5); - - tree.remove(20); - - // You can add expectations to check Red-Black properties - // For example, checking that there are no two consecutive red nodes in a path, etc. - expect(tree.isRBTree()).toBe(true); - }); - - it('should handle edge cases', () => { - const tree = new RBTree>(); - - - // Test with an empty tree - expect(tree.size).toBe(0); - expect(tree.isRBTree()).toBe(true); - - // Test deleting non-existent elements - tree.add(10); - tree.remove(20); - expect(tree.size).toBe(1); - - // Test deleting the root element - tree.remove(10); - expect(tree.size).toBe(0); - }); -}); -