From 57a95e94dbb9e09c3a082af722fb458cda87b612 Mon Sep 17 00:00:00 2001 From: Revone Date: Tue, 7 Nov 2023 19:57:35 +0800 Subject: [PATCH] [rb-tree] The red-black tree has been perfectly implemented and inherits from a BST. It has also passed a certain level of testing. [binary-tree] All traversal methods are compatible with sentinel nodes in the red-black tree. --- .../binary-tree/binary-tree.ts | 70 +++++++++++-------- src/data-structures/binary-tree/rb-tree.ts | 2 +- .../binary-tree/rb-tree.test.ts | 55 ++++++++++----- 3 files changed, 79 insertions(+), 48 deletions(-) diff --git a/src/data-structures/binary-tree/binary-tree.ts b/src/data-structures/binary-tree/binary-tree.ts index 8047b2e..8e6cd5f 100644 --- a/src/data-structures/binary-tree/binary-tree.ts +++ b/src/data-structures/binary-tree/binary-tree.ts @@ -896,11 +896,11 @@ export class BinaryTree = BinaryTreeNode if (cur !== undefined) { ans.push(callback(cur)); if (includeNull) { - cur !== null && cur.left !== undefined && _traverse(cur.left); - cur !== null && cur.right !== undefined && _traverse(cur.right); + cur && this.isNodeOrNull(cur.left) && _traverse(cur.left); + cur && this.isNodeOrNull(cur.right) && _traverse(cur.right); } else { - cur !== null && cur.left && _traverse(cur.left); - cur !== null && cur.right && _traverse(cur.right); + cur && cur.left && _traverse(cur.left); + cur && cur.right && _traverse(cur.right); } } }; @@ -914,17 +914,29 @@ export class BinaryTree = BinaryTreeNode if (cur !== undefined) { ans.push(callback(cur)); if (includeNull) { - cur !== null && cur.right !== undefined && stack.push(cur.right); - cur !== null && cur.left !== undefined && stack.push(cur.left); + cur && this.isNodeOrNull(cur.right) && stack.push(cur.right); + cur && this.isNodeOrNull(cur.left) && stack.push(cur.left); } else { - cur !== null && cur.right && stack.push(cur.right); - cur !== null && cur.left && stack.push(cur.left); + cur && cur.right && stack.push(cur.right); + cur && cur.left && stack.push(cur.left); } } } } return ans; } + + isNode(node: any): node is N { + return node instanceof BinaryTreeNode && node.key.toString() !== 'NaN'; + } + + isNIL(node: any) { + return node instanceof BinaryTreeNode && node.key.toString() === 'NaN'; + } + + isNodeOrNull(node: any): node is (N | null){ + return this.isNode(node) || node === null; + } dfs>( callback?: C, @@ -980,35 +992,35 @@ export class BinaryTree = BinaryTreeNode switch (pattern) { case 'in': if (includeNull) { - if (node && node.left !== undefined) _traverse(node.left); - ans.push(callback(node)); - if (node && node.right !== undefined) _traverse(node.right); + if (node && this.isNodeOrNull(node.left)) _traverse(node.left); + this.isNodeOrNull(node) && ans.push(callback(node)); + if (node && this.isNodeOrNull(node.right)) _traverse(node.right); } else { if (node && node.left) _traverse(node.left); - ans.push(callback(node)); + this.isNode(node) && ans.push(callback(node)); if (node && node.right) _traverse(node.right); } break; case 'pre': if (includeNull) { - ans.push(callback(node)); - if (node && node.left !== undefined) _traverse(node.left); - if (node && node.right !== undefined) _traverse(node.right); + this.isNodeOrNull(node) && ans.push(callback(node)); + if (node && this.isNodeOrNull(node.left)) _traverse(node.left); + if (node && this.isNodeOrNull(node.right)) _traverse(node.right); } else { - ans.push(callback(node)); + this.isNode(node) && ans.push(callback(node)); if (node && node.left) _traverse(node.left); if (node && node.right) _traverse(node.right); } break; case 'post': if (includeNull) { - if (node && node.left !== undefined) _traverse(node.left); - if (node && node.right !== undefined) _traverse(node.right); - ans.push(callback(node)); + if (node && this.isNodeOrNull(node.left)) _traverse(node.left); + if (node && this.isNodeOrNull(node.right)) _traverse(node.right); + this.isNodeOrNull(node) && ans.push(callback(node)); } else { if (node && node.left) _traverse(node.left); if (node && node.right) _traverse(node.right); - ans.push(callback(node)); + this.isNode(node) && ans.push(callback(node)); } break; @@ -1022,7 +1034,7 @@ export class BinaryTree = BinaryTreeNode while (stack.length > 0) { const cur = stack.pop(); - if (cur === undefined) continue; + if (cur === undefined || this.isNIL(cur.node)) continue; if (includeNull) { if (cur.node === undefined) continue; } else { @@ -1115,8 +1127,8 @@ export class BinaryTree = BinaryTreeNode ans.push(callback(current)); if (includeNull) { - if (current && current.left !== undefined) queue.push(current.left); - if (current && current.right !== undefined) queue.push(current.right); + if (current && this.isNodeOrNull(current.left)) queue.push(current.left); + if (current && this.isNodeOrNull(current.right)) queue.push(current.right); } else { if (current.left) queue.push(current.left); if (current.right) queue.push(current.right); @@ -1136,8 +1148,8 @@ export class BinaryTree = BinaryTreeNode ans.push(callback(current)); if (includeNull) { - if (current !== null && current.left !== undefined) queue.push(current.left); - if (current !== null && current.right !== undefined) queue.push(current.right); + if (current && this.isNodeOrNull(current.left)) queue.push(current.left); + if (current && this.isNodeOrNull(current.right)) queue.push(current.right); } else { if (current.left) queue.push(current.left); if (current.right) queue.push(current.right); @@ -1199,8 +1211,8 @@ export class BinaryTree = BinaryTreeNode if (!levelsNodes[level]) levelsNodes[level] = []; levelsNodes[level].push(callback(node)); if (includeNull) { - if (node && node.left !== undefined) _recursive(node.left, level + 1); - if (node && node.right !== undefined) _recursive(node.right, level + 1); + if (node && this.isNodeOrNull(node.left)) _recursive(node.left, level + 1); + if (node && this.isNodeOrNull(node.right)) _recursive(node.right, level + 1); } else { if (node && node.left) _recursive(node.left, level + 1); if (node && node.right) _recursive(node.right, level + 1); @@ -1219,8 +1231,8 @@ export class BinaryTree = BinaryTreeNode levelsNodes[level].push(callback(node)); if (includeNull) { - if (node && node.right !== undefined) stack.push([node.right, level + 1]); - if (node && node.left !== undefined) stack.push([node.left, level + 1]); + if (node && this.isNodeOrNull(node.right)) stack.push([node.right, level + 1]); + if (node && this.isNodeOrNull(node.left)) stack.push([node.left, level + 1]); } else { if (node && node.right) stack.push([node.right, level + 1]); if (node && node.left) stack.push([node.left, level + 1]); diff --git a/src/data-structures/binary-tree/rb-tree.ts b/src/data-structures/binary-tree/rb-tree.ts index e5512d2..c0356c0 100644 --- a/src/data-structures/binary-tree/rb-tree.ts +++ b/src/data-structures/binary-tree/rb-tree.ts @@ -177,7 +177,7 @@ export class RedBlackTree = RBTreeNode { expect(node225S?.parent?.key).toBe(155); // TODO // expect(tree.getNode(0)).toBe(undefined); - tree.add(1); tree.add(2); tree.add(3); tree.add(4); - tree.add(5); tree.add(6); - tree.add(7); - tree.add(8); tree.add(9); - tree.add(10); tree.add(11); - tree.add(12); tree.add(13); tree.add(14); - tree.add(15); tree.add(16); tree.add(17); tree.add(18); @@ -425,22 +418,48 @@ describe('RedBlackTree', () => { tree.add(110); isDebug && tree.print(); - // console.log(tree.dfs()) - // console.log(tree.isBST()) + + expect(tree.dfs()).toEqual([ + 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 22, 23, 25, 28, 33, 50, 110, 111, + 155, 225 + ]) + + expect(tree.isBST()).toBe(true); }); it('should fix the tree after insertion and deletion', () => { - for (let i = 0; i < 1000; i++) { - tree.add(getRandomInt(-100, 1000)); + for (let i = 0; i < 100; i++) { + tree.add(i); } - for (let i = 0; i < 1000; i++) { - tree.delete(getRandomInt(-100, 1000)); + for (let i = 0; i < 49; i++) { + tree.delete(i); } - for (let i = 0; i < 1000; i++) { - tree.add(getRandomInt(-100, 1000)); - tree.delete(getRandomInt(-100, 1000)); - } - // console.log(tree.dfs( n => n.key, "in", tree.root, IterationType.RECURSIVE)) + + expect(tree.size).toBe(51); + expect(tree.isBST()).toBe(true); + expect(tree.dfs( n => n.key, "in", tree.root, IterationType.ITERATIVE)).toEqual([ + 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, + 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, + 93, 94, 95, 96, 97, 98, 99 + ]) }); + + it('should fix the tree after large scale insertion and deletion', () => { + for (let i = 0; i < 10000; i++) { + tree.add(i); + } + for (let i = 0; i < 10000; i++) { + tree.delete(i); + } + + expect(tree.size).toBe(0); + expect(tree.isBST()).toBe(true); + expect(tree.dfs( n => n.key, "in", tree.root, IterationType.ITERATIVE)).toEqual([]) + }); + });