From 2bdfd79b9fe0b055431a6933528a7992d197f65a Mon Sep 17 00:00:00 2001 From: Revone Date: Tue, 10 Oct 2023 10:28:23 +0800 Subject: [PATCH] [rbtree] implemented, but with bugs --- package-lock.json | 128 ++-- src/data-structures/binary-tree/rb-tree.ts | 626 ++++++++---------- test/integration/index.html | 3 +- .../binary-tree/avl-tree.test.ts | 2 +- .../binary-tree/rb-tree.test.ts | 203 +++++- 5 files changed, 529 insertions(+), 433 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9fec503..1c6302e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "data-structure-typed", - "version": "1.34.2", + "version": "1.34.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "data-structure-typed", - "version": "1.34.2", + "version": "1.34.5", "license": "MIT", "devDependencies": { "@types/benchmark": "^2.1.3", @@ -757,9 +757,9 @@ "dev": true }, "node_modules/@eslint/js": { - "version": "8.50.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.50.0.tgz", - "integrity": "sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ==", + "version": "8.51.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.51.0.tgz", + "integrity": "sha512-HxjQ8Qn+4SI3/AFv6sOrDB+g6PpUTDwSJiQqOrnneEk8L71161srI9gjzzZvYVbzHiVg/BvcH95+cK/zfIt4pg==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1675,9 +1675,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.8.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.2.tgz", - "integrity": "sha512-Vvycsc9FQdwhxE3y3DzeIxuEJbWGDsnrxvMADzTDF/lcdR9/K+AQIeAghTQsHtotg/q0j3WEOYS/jQgSdWue3w==", + "version": "20.8.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.3.tgz", + "integrity": "sha512-jxiZQFpb+NlH5kjW49vXxvxTjeeqlbsnTAdBTKpzEdPs9itay7MscYXz3Fo9VYFEsfQ6LJFitHad3faerLAjCw==", "dev": true }, "node_modules/@types/semver": { @@ -1693,9 +1693,9 @@ "dev": true }, "node_modules/@types/yargs": { - "version": "17.0.26", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.26.tgz", - "integrity": "sha512-Y3vDy2X6zw/ZCumcwLpdhM5L7jmyGpmBCTYMHDLqT2IKVMYRRLdv6ZakA+wxhra6Z/3bwhNbNl9bDGXaFU+6rw==", + "version": "17.0.28", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.28.tgz", + "integrity": "sha512-N3e3fkS86hNhtk6BEnc0rj3zcehaxx8QWhCROJkqpl5Zaoi7nAic3jH8q94jVD3zu5LGk+PUB6KAiDmimYOEQw==", "dev": true, "dependencies": { "@types/yargs-parser": "*" @@ -2393,12 +2393,12 @@ } }, "node_modules/avl-tree-typed": { - "version": "1.34.1", - "resolved": "https://registry.npmjs.org/avl-tree-typed/-/avl-tree-typed-1.34.1.tgz", - "integrity": "sha512-5SgeKmOZivy6krk/KoKswaQVdaEXSZCkGWq+5yjfnszYKpwZzkijN/CAw1r/S1keF7L9cNA+wUMaE6XBE/KZOQ==", + "version": "1.34.5", + "resolved": "https://registry.npmjs.org/avl-tree-typed/-/avl-tree-typed-1.34.5.tgz", + "integrity": "sha512-MlFgQHU0RYJJwTI2aXGRxFapjDODYqpZr+V5OGHS/NSbIQkwyJIN5Qg9YngWhqJGkQ/gdRP8zi4weFBVSwXrWA==", "dev": true, "dependencies": { - "data-structure-typed": "^1.34.1" + "data-structure-typed": "^1.34.5" } }, "node_modules/babel-jest": { @@ -2583,12 +2583,12 @@ } }, "node_modules/binary-tree-typed": { - "version": "1.34.1", - "resolved": "https://registry.npmjs.org/binary-tree-typed/-/binary-tree-typed-1.34.1.tgz", - "integrity": "sha512-ar2ROpvmPVN9ZlEP094lkV0mZdP+8cqWjUaLzQHuMk6jXUSfo3i95iIFJCVTqX0v3tZpCH51OfQoJxBX130eQQ==", + "version": "1.34.5", + "resolved": "https://registry.npmjs.org/binary-tree-typed/-/binary-tree-typed-1.34.5.tgz", + "integrity": "sha512-hADhuYpkUK1FogYAu2DI+X9RWwA5Jj/FU3lwghN+Mgua7i3DnEdDla5ZwtxnpGwXF1yAfzQo73+HgHW3Y67ksA==", "dev": true, "dependencies": { - "data-structure-typed": "^1.34.1" + "data-structure-typed": "^1.34.5" } }, "node_modules/brace-expansion": { @@ -2667,12 +2667,12 @@ } }, "node_modules/bst-typed": { - "version": "1.34.1", - "resolved": "https://registry.npmjs.org/bst-typed/-/bst-typed-1.34.1.tgz", - "integrity": "sha512-z5K3SXz71CufxYEh3ztzzfubfsJSFQW6J6p2zJL8EZrbLACFOmP6/B/3sFFsWzHiFuTrn/imWRycYQbjDR/YPw==", + "version": "1.34.5", + "resolved": "https://registry.npmjs.org/bst-typed/-/bst-typed-1.34.5.tgz", + "integrity": "sha512-+SlQtIyjca0YevEMSgvjODHZ//lBGX54k6HARyAPSBeK8NID0m5Zh/DXhU3BNvSwrccKy7RHvsb55JLW7Xx9vw==", "dev": true, "dependencies": { - "data-structure-typed": "^1.34.1" + "data-structure-typed": "^1.34.5" } }, "node_modules/buffer-from": { @@ -3024,9 +3024,9 @@ } }, "node_modules/data-structure-typed": { - "version": "1.34.1", - "resolved": "https://registry.npmjs.org/data-structure-typed/-/data-structure-typed-1.34.1.tgz", - "integrity": "sha512-/qNoKl1/cPpZUR7V8qMNS+WOqLIz4RVEuVZeOilJGCfWerbx/bPEtQsqEKMXLwclQJ4KLKSAoblJNPCIEuUYRw==", + "version": "1.34.5", + "resolved": "https://registry.npmjs.org/data-structure-typed/-/data-structure-typed-1.34.5.tgz", + "integrity": "sha512-5JtTtvzMHadRzNWHiC+jlaSIHmyC4E4Q9P4ShdnT4SfLQkHWjHFTNfrB8VEdKrQa+mZqqGPHMKEdUWCVGXG10Q==", "dev": true }, "node_modules/debug": { @@ -3107,9 +3107,9 @@ } }, "node_modules/dependency-cruiser": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/dependency-cruiser/-/dependency-cruiser-14.1.0.tgz", - "integrity": "sha512-JF7F0SFG4K5vXmUMvgYHKQnMuU2JzO18/+r/hTuaGEr3KTlMYkR16WNc+WDqS0y5fjq8khDy/WKO4bR5xhw2sQ==", + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/dependency-cruiser/-/dependency-cruiser-14.1.1.tgz", + "integrity": "sha512-npNLWv11pMH9BW4GBLuA5p6KYOXA9UjVDKQ4DzorEhAac5BS1J23K5I2WpEfkJMpwl9PKMsF4T/GDLSq3pogTw==", "dev": true, "dependencies": { "acorn": "8.10.0", @@ -3136,7 +3136,7 @@ "semver-try-require": "6.2.3", "teamcity-service-messages": "0.1.14", "tsconfig-paths-webpack-plugin": "4.1.0", - "watskeburt": "1.0.1", + "watskeburt": "2.0.0", "wrap-ansi": "8.1.0" }, "bin": { @@ -3209,9 +3209,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.543", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.543.tgz", - "integrity": "sha512-t2ZP4AcGE0iKCCQCBx/K2426crYdxD3YU6l0uK2EO3FZH0pbC4pFz/sZm2ruZsND6hQBTcDWWlo/MLpiOdif5g==", + "version": "1.4.544", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.544.tgz", + "integrity": "sha512-54z7squS1FyFRSUqq/knOFSptjjogLZXbKcYk3B0qkE1KZzvqASwRZnY2KzZQJqIYLVD38XZeoiMRflYSwyO4w==", "dev": true }, "node_modules/emittery": { @@ -3387,15 +3387,15 @@ } }, "node_modules/eslint": { - "version": "8.50.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.50.0.tgz", - "integrity": "sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg==", + "version": "8.51.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.51.0.tgz", + "integrity": "sha512-2WuxRZBrlwnXi+/vFSJyjMqrNjtJqiasMzehF0shoLaW7DzS3/9Yvrmq5JiT66+pNjiX4UBnLDiKHcWAr/OInA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "8.50.0", + "@eslint/js": "8.51.0", "@humanwhocodes/config-array": "^0.11.11", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -3966,12 +3966,12 @@ } }, "node_modules/flat-cache": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.0.tgz", - "integrity": "sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.1.tgz", + "integrity": "sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==", "dev": true, "dependencies": { - "flatted": "^3.2.7", + "flatted": "^3.2.9", "keyv": "^4.5.3", "rimraf": "^3.0.2" }, @@ -4356,12 +4356,12 @@ } }, "node_modules/heap-typed": { - "version": "1.34.1", - "resolved": "https://registry.npmjs.org/heap-typed/-/heap-typed-1.34.1.tgz", - "integrity": "sha512-yXOmzX/6xcX3CdIoM/RmaPpzz4oftZtSfOU8ru21+bZUzCYiRs5cY7oXGXux9LmUgowrxQgvNveRD1AZYPzXhQ==", + "version": "1.34.5", + "resolved": "https://registry.npmjs.org/heap-typed/-/heap-typed-1.34.5.tgz", + "integrity": "sha512-6THYA+LcDp6peKWQ4lg/xLPBiQBTc/Ym0frGiH43lKfINK8ilXnEYy5/KrCK6SBO9Marj7CXDuTzHXo4wbbJWw==", "dev": true, "dependencies": { - "data-structure-typed": "^1.34.1" + "data-structure-typed": "^1.34.5" } }, "node_modules/html-escaper": { @@ -6210,9 +6210,9 @@ "dev": true }, "node_modules/keyv": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz", - "integrity": "sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==", + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "dependencies": { "json-buffer": "3.0.1" @@ -7829,15 +7829,16 @@ } }, "node_modules/ts-loader": { - "version": "9.4.4", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.4.tgz", - "integrity": "sha512-MLukxDHBl8OJ5Dk3y69IsKVFRA/6MwzEqBgh+OXMPB/OD01KQuWPFd1WAQP8a5PeSCAxfnkhiuWqfmFJzJQt9w==", + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.0.tgz", + "integrity": "sha512-LLlB/pkB4q9mW2yLdFMnK3dEHbrBjeZTYguaaIfusyojBgAGf5kF+O6KcWqiGzWqHk0LBsoolrp4VftEURhybg==", "dev": true, "dependencies": { "chalk": "^4.1.0", "enhanced-resolve": "^5.0.0", "micromatch": "^4.0.0", - "semver": "^7.3.4" + "semver": "^7.3.4", + "source-map": "^0.7.4" }, "engines": { "node": ">=12.0.0" @@ -7896,6 +7897,15 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/ts-loader/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, "node_modules/tsconfig-paths": { "version": "3.14.2", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", @@ -8114,9 +8124,9 @@ } }, "node_modules/typedoc": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.1.tgz", - "integrity": "sha512-c2ye3YUtGIadxN2O6YwPEXgrZcvhlZ6HlhWZ8jQRNzwLPn2ylhdGqdR8HbyDRyALP8J6lmSANILCkkIdNPFxqA==", + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.2.tgz", + "integrity": "sha512-286F7BeATBiWe/qC4PCOCKlSTwfnsLbC/4cZ68oGBbvAqb9vV33quEOXx7q176OXotD+JdEerdQ1OZGJ818lnA==", "dev": true, "dependencies": { "lunr": "^2.3.9", @@ -8287,15 +8297,15 @@ } }, "node_modules/watskeburt": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/watskeburt/-/watskeburt-1.0.1.tgz", - "integrity": "sha512-MOvC8vf3hAVo1HPF/pkba7065mt6A/P9unLlFvYhZ7Yyuht16tmfCYi/LqHABG4hIRMZCbvY8eDWHPy81eSADA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/watskeburt/-/watskeburt-2.0.0.tgz", + "integrity": "sha512-RJ961Bcw9sfHr1NqZwvcFBYWo6bN9xE1CeBy6LigLqpzzrdnvsMT5HFg2JhOe4ioDOrCndjNa3tsErIVZtCc3g==", "dev": true, "bin": { "watskeburt": "dist/cli.js" }, "engines": { - "node": "^16.14||>=18" + "node": "^18||>=20" } }, "node_modules/webidl-conversions": { diff --git a/src/data-structures/binary-tree/rb-tree.ts b/src/data-structures/binary-tree/rb-tree.ts index f230e7c..4892ef9 100644 --- a/src/data-structures/binary-tree/rb-tree.ts +++ b/src/data-structures/binary-tree/rb-tree.ts @@ -1,11 +1,10 @@ -import {BinaryTreeNodeId, RBColor, RBTreeNodeNested, RBTreeOptions} from '../../types'; +import {BinaryTreeDeletedResult, BinaryTreeNodeId, RBColor, RBTreeNodeNested, RBTreeOptions} from '../../types'; import {IRBTree, IRBTreeNode} from '../../interfaces'; import {BST, BSTNode} from './bst'; export class RBTreeNode = RBTreeNodeNested> extends BSTNode - implements IRBTreeNode -{ + implements IRBTreeNode { private _color: RBColor; constructor(id: BinaryTreeNodeId, val?: V) { @@ -31,336 +30,293 @@ export class RBTree = RBTreeNode> extends BST< return new RBTreeNode(id, val) as N; } - // override add(idOrNode: BinaryTreeNodeId | N | null, val?: N['val']): N | null | undefined { - // const inserted = super.add(idOrNode, 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(nodeOrId: BinaryTreeNodeId | N): BinaryTreeDeletedResult[] { - // const node = this.get(nodeOrId); - // 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 2947fff..6c62ea4 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; }); }); + + +