refactor: rename NIL of RedBlackTree to Sentinel

This commit is contained in:
Revone 2023-11-21 17:44:22 +08:00
parent 54c74f9af6
commit 01b969edca
2 changed files with 59 additions and 59 deletions

View file

@ -34,14 +34,14 @@ export class RedBlackTreeNode<V = any, N extends RedBlackTreeNode<V, N> = RedBla
/**
* 1. Each node is either red or black.
* 2. The root node is always black.
* 3. Leaf nodes are typically NIL nodes and are considered black.
* 3. Leaf nodes are typically Sentinel nodes and are considered black.
* 4. Red nodes must have black children.
* 5. Black balance: Every path from any node to each of its leaf nodes contains the same number of black nodes.
*/
export class RedBlackTree<V = any, N extends RedBlackTreeNode<V, N> = RedBlackTreeNode<V, RedBlackTreeNodeNested<V>>>
extends BST<V, N>
implements IBinaryTree<V, N> {
NIL: N = new RedBlackTreeNode<V>(NaN) as unknown as N;
Sentinel: N = new RedBlackTreeNode<V>(NaN) as unknown as N;
/**
* The constructor function initializes a Red-Black Tree with an optional set of options.
@ -50,7 +50,7 @@ export class RedBlackTree<V = any, N extends RedBlackTreeNode<V, N> = RedBlackTr
*/
constructor(options?: RBTreeOptions) {
super(options);
this._root = this.NIL;
this._root = this.Sentinel;
}
protected _root: N;
@ -95,13 +95,13 @@ export class RedBlackTree<V = any, N extends RedBlackTreeNode<V, N> = RedBlackTr
return;
}
node.left = this.NIL;
node.right = this.NIL;
node.left = this.Sentinel;
node.right = this.Sentinel;
let y: N | undefined = undefined;
let x: N | undefined = this.root;
while (x !== this.NIL) {
while (x !== this.Sentinel) {
y = x;
if (x) {
if (node.key < x.key) {
@ -170,9 +170,9 @@ export class RedBlackTree<V = any, N extends RedBlackTreeNode<V, N> = RedBlackTr
const ans: BiTreeDeleteResult<N>[] = [];
if (identifier === null) return ans;
const helper = (node: N | undefined): void => {
let z: N = this.NIL;
let z: N = this.Sentinel;
let x: N | undefined, y: N;
while (node !== this.NIL) {
while (node !== this.Sentinel) {
if (node && callback(node) === identifier) {
z = node;
}
@ -184,17 +184,17 @@ export class RedBlackTree<V = any, N extends RedBlackTreeNode<V, N> = RedBlackTr
}
}
if (z === this.NIL) {
if (z === this.Sentinel) {
this._size--;
return;
}
y = z;
let yOriginalColor: number = y.color;
if (z.left === this.NIL) {
if (z.left === this.Sentinel) {
x = z.right;
this._rbTransplant(z, z.right!);
} else if (z.right === this.NIL) {
} else if (z.right === this.Sentinel) {
x = z.left;
this._rbTransplant(z, z.left!);
} else {
@ -225,7 +225,7 @@ export class RedBlackTree<V = any, N extends RedBlackTreeNode<V, N> = RedBlackTr
}
override isRealNode(node: N | undefined): node is N {
return node !== this.NIL && node !== undefined;
return node !== this.Sentinel && node !== undefined;
}
getNode<C extends BTNCallback<N, BTNKey>>(
@ -300,12 +300,12 @@ export class RedBlackTree<V = any, N extends RedBlackTreeNode<V, N> = RedBlackTr
* @returns the successor of the given RedBlackTreeNode.
*/
override getSuccessor(x: N): N | undefined {
if (x.right !== this.NIL) {
if (x.right !== this.Sentinel) {
return this.getLeftMost(x.right) ?? undefined;
}
let y: N | undefined = x.parent;
while (y !== this.NIL && y !== undefined && x === y.right) {
while (y !== this.Sentinel && y !== undefined && x === y.right) {
x = y;
y = y.parent;
}
@ -327,12 +327,12 @@ export class RedBlackTree<V = any, N extends RedBlackTreeNode<V, N> = RedBlackTr
* @returns the predecessor of the given RedBlackTreeNode 'x'.
*/
override getPredecessor(x: N): N {
if (x.left !== this.NIL) {
if (x.left !== this.Sentinel) {
return this.getRightMost(x.left!)!;
}
let y: N | undefined = x.parent;
while (y !== this.NIL && x === y!.left) {
while (y !== this.Sentinel && x === y!.left) {
x = y!;
y = y!.parent;
}
@ -341,7 +341,7 @@ export class RedBlackTree<V = any, N extends RedBlackTreeNode<V, N> = RedBlackTr
}
override clear() {
this._root = this.NIL;
this._root = this.Sentinel;
this._size = 0;
}
@ -368,7 +368,7 @@ export class RedBlackTree<V = any, N extends RedBlackTreeNode<V, N> = RedBlackTr
if (x.right) {
const y: N = x.right;
x.right = y.left;
if (y.left !== this.NIL) {
if (y.left !== this.Sentinel) {
if (y.left) y.left.parent = x;
}
y.parent = x.parent;
@ -401,7 +401,7 @@ export class RedBlackTree<V = any, N extends RedBlackTreeNode<V, N> = RedBlackTr
if (x.left) {
const y: N = x.left;
x.left = y.right;
if (y.right !== this.NIL) {
if (y.right !== this.Sentinel) {
if (y.right) y.right.parent = x;
}
y.parent = x.parent;

View file

@ -67,7 +67,7 @@ describe('RedBlackTree', () => {
it('should handle an empty tree', () => {
const minNode = tree.getLeftMost(tree.root);
expect(minNode).toBe(tree.NIL);
expect(minNode).toBe(tree.Sentinel);
});
});
@ -85,7 +85,7 @@ describe('RedBlackTree', () => {
it('should handle an empty tree', () => {
const maxNode = tree.getRightMost(tree.root);
expect(maxNode).toBe(tree.NIL);
expect(maxNode).toBe(tree.Sentinel);
});
});
@ -109,7 +109,7 @@ describe('RedBlackTree', () => {
const node = tree.getNode(10);
const successorNode = tree.getSuccessor(node!);
// TODO not sure if it should be undefined or tree.NIL
// TODO not sure if it should be undefined or tree.Sentinel
expect(successorNode).toBe(undefined);
});
});
@ -134,7 +134,7 @@ describe('RedBlackTree', () => {
const node = tree.getNode(20);
const predecessorNode = tree.getPredecessor(node!);
// TODO not sure if it should be tree.NIL or something else.
// TODO not sure if it should be tree.Sentinel or something else.
expect(predecessorNode).toBe(tree.getNode(10));
});
});
@ -235,28 +235,28 @@ describe('RedBlackTree', () => {
expect(node5F?.parent).toBe(node10F);
expect(node15F?.key).toBe(15);
expect(node15F?.color).toBe(RBTNColor.RED);
expect(node15F?.left).toBe(tree.NIL);
expect(node15F?.right).toBe(tree.NIL);
expect(node15F?.left).toBe(tree.Sentinel);
expect(node15F?.right).toBe(tree.Sentinel);
expect(node15F?.parent).toBe(node20F);
expect(node21F?.key).toBe(21);
expect(node21F?.color).toBe(RBTNColor.RED);
expect(node21F?.left).toBe(tree.NIL);
expect(node21F?.right).toBe(tree.NIL);
expect(node21F?.left).toBe(tree.Sentinel);
expect(node21F?.right).toBe(tree.Sentinel);
expect(node21F?.parent).toBe(node20F);
expect(node6F?.key).toBe(6);
expect(node6F?.color).toBe(RBTNColor.RED);
expect(node6F?.left).toBe(tree.NIL);
expect(node6F?.right).toBe(tree.NIL);
expect(node6F?.left).toBe(tree.Sentinel);
expect(node6F?.right).toBe(tree.Sentinel);
expect(node6F?.parent).toBe(node5F);
expect(node2F?.key).toBe(2);
expect(node2F?.color).toBe(RBTNColor.RED);
expect(node2F?.left).toBe(tree.NIL);
expect(node2F?.right).toBe(tree.NIL);
expect(node2F?.left).toBe(tree.Sentinel);
expect(node2F?.right).toBe(tree.Sentinel);
expect(node2F?.parent).toBe(node5F);
expect(node15F?.key).toBe(15);
expect(node15F?.color).toBe(RBTNColor.RED);
expect(node15F?.left).toBe(tree.NIL);
expect(node15F?.right).toBe(tree.NIL);
expect(node15F?.left).toBe(tree.Sentinel);
expect(node15F?.right).toBe(tree.Sentinel);
expect(node15F?.parent).toBe(node20F);
tree.delete(5);
node10F = tree.getNode(10);
@ -279,28 +279,28 @@ describe('RedBlackTree', () => {
expect(node5F).toBe(undefined);
expect(node15F?.key).toBe(15);
expect(node15F?.color).toBe(RBTNColor.RED);
expect(node15F?.left).toBe(tree.NIL);
expect(node15F?.right).toBe(tree.NIL);
expect(node15F?.left).toBe(tree.Sentinel);
expect(node15F?.right).toBe(tree.Sentinel);
expect(node15F?.parent).toBe(node20F);
expect(node21F?.key).toBe(21);
expect(node21F?.color).toBe(RBTNColor.RED);
expect(node21F?.left).toBe(tree.NIL);
expect(node21F?.right).toBe(tree.NIL);
expect(node21F?.left).toBe(tree.Sentinel);
expect(node21F?.right).toBe(tree.Sentinel);
expect(node21F?.parent).toBe(node20F);
expect(node6F?.key).toBe(6);
expect(node6F?.color).toBe(RBTNColor.BLACK);
expect(node6F?.left).toBe(node2F);
expect(node6F?.right).toBe(tree.NIL);
expect(node6F?.right).toBe(tree.Sentinel);
expect(node6F?.parent).toBe(node10F);
expect(node2F?.key).toBe(2);
expect(node2F?.color).toBe(RBTNColor.RED);
expect(node2F?.left).toBe(tree.NIL);
expect(node2F?.right).toBe(tree.NIL);
expect(node2F?.left).toBe(tree.Sentinel);
expect(node2F?.right).toBe(tree.Sentinel);
expect(node2F?.parent).toBe(node6F);
expect(node15F?.key).toBe(15);
expect(node15F?.color).toBe(RBTNColor.RED);
expect(node15F?.left).toBe(tree.NIL);
expect(node15F?.right).toBe(tree.NIL);
expect(node15F?.left).toBe(tree.Sentinel);
expect(node15F?.right).toBe(tree.Sentinel);
expect(node15F?.parent).toBe(node20F);
tree.delete(20);
node10F = tree.getNode(10);
@ -319,28 +319,28 @@ describe('RedBlackTree', () => {
expect(node5F).toBe(undefined);
expect(node15F?.key).toBe(15);
expect(node15F?.color).toBe(RBTNColor.RED);
expect(node15F?.left).toBe(tree.NIL);
expect(node15F?.right).toBe(tree.NIL);
expect(node15F?.left).toBe(tree.Sentinel);
expect(node15F?.right).toBe(tree.Sentinel);
expect(node15F?.parent).toBe(node21F);
expect(node21F?.key).toBe(21);
expect(node21F?.color).toBe(RBTNColor.BLACK);
expect(node21F?.left).toBe(node15F);
expect(node21F?.right).toBe(tree.NIL);
expect(node21F?.right).toBe(tree.Sentinel);
expect(node21F?.parent).toBe(node10F);
expect(node6F?.key).toBe(6);
expect(node6F?.color).toBe(RBTNColor.BLACK);
expect(node6F?.left).toBe(node2F);
expect(node6F?.right).toBe(tree.NIL);
expect(node6F?.right).toBe(tree.Sentinel);
expect(node6F?.parent).toBe(node10F);
expect(node2F?.key).toBe(2);
expect(node2F?.color).toBe(RBTNColor.RED);
expect(node2F?.left).toBe(tree.NIL);
expect(node2F?.right).toBe(tree.NIL);
expect(node2F?.left).toBe(tree.Sentinel);
expect(node2F?.right).toBe(tree.Sentinel);
expect(node2F?.parent).toBe(node6F);
expect(node15F?.key).toBe(15);
expect(node15F?.color).toBe(RBTNColor.RED);
expect(node15F?.left).toBe(tree.NIL);
expect(node15F?.right).toBe(tree.NIL);
expect(node15F?.left).toBe(tree.Sentinel);
expect(node15F?.right).toBe(tree.Sentinel);
expect(node15F?.parent).toBe(node21F);
});
@ -350,8 +350,8 @@ describe('RedBlackTree', () => {
tree.add(5);
tree.add(15);
const node15F = tree.getNode(15);
expect(node15F?.left).toBe(tree.NIL);
expect(node15F?.right).toBe(tree.NIL);
expect(node15F?.left).toBe(tree.Sentinel);
expect(node15F?.right).toBe(tree.Sentinel);
expect(node15F?.parent).toBe(tree.getNode(5));
tree.add(25);
@ -366,8 +366,8 @@ describe('RedBlackTree', () => {
tree.add(155);
tree.add(225);
const node225F = tree.getNode(225);
expect(node225F?.left).toBe(tree.NIL);
expect(node225F?.right).toBe(tree.NIL);
expect(node225F?.left).toBe(tree.Sentinel);
expect(node225F?.right).toBe(tree.Sentinel);
expect(node225F?.parent?.key).toBe(155);
tree.add(7);
@ -393,14 +393,14 @@ describe('RedBlackTree', () => {
const node50 = tree.getNode(50);
expect(node50?.key).toBe(50);
expect(node50?.left?.key).toBe(33);
expect(node50?.right).toBe(tree.NIL);
expect(node50?.right).toBe(tree.Sentinel);
const node15Fo = tree.getNode(15);
expect(node15Fo?.key).toBe(15);
expect(node15Fo?.left).toBe(tree.NIL);
expect(node15Fo?.left).toBe(tree.Sentinel);
const node225S = tree.getNode(225);
expect(node225S?.left).toBe(tree.NIL);
expect(node225S?.right).toBe(tree.NIL);
expect(node225S?.left).toBe(tree.Sentinel);
expect(node225S?.right).toBe(tree.Sentinel);
expect(node225S?.parent?.key).toBe(155);
// TODO
// expect(tree.getNode(0)).toBe(undefined);
@ -465,7 +465,7 @@ describe('RedBlackTree', () => {
tree.delete(getRandomInt(-100, 1000));
}
// TODO there is a bug when dfs the tree with NIL node
// TODO there is a bug when dfs the tree with Sentinel node
// expect(tree.isBST()).toBe(true);
});
const { HUNDRED_THOUSAND } = magnitude;