typescript-ds-lib
Version:
A collection of TypeScript data structure implementations
350 lines • 11 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RedBlackTree = void 0;
const base_collection_1 = require("./base-collection");
const utils_1 = require("./utils");
var Color;
(function (Color) {
Color[Color["RED"] = 0] = "RED";
Color[Color["BLACK"] = 1] = "BLACK";
})(Color || (Color = {}));
class RBNode {
key;
value;
color;
left;
right;
parent;
constructor(key, value) {
this.key = key;
this.value = value;
this.color = Color.RED;
this.left = null;
this.right = null;
this.parent = null;
}
}
class RedBlackTree extends base_collection_1.BaseCollection {
root;
nodeCount;
comparator;
constructor(comparator = (a, b) => a < b) {
super();
this.root = null;
this.nodeCount = 0;
this.comparator = comparator;
}
rotateLeft(node) {
const rightChild = node.right;
node.right = rightChild.left;
if (rightChild.left !== null) {
rightChild.left.parent = node;
}
rightChild.parent = node.parent;
if (node.parent === null) {
this.root = rightChild;
}
else if (node === node.parent.left) {
node.parent.left = rightChild;
}
else {
node.parent.right = rightChild;
}
rightChild.left = node;
node.parent = rightChild;
}
rotateRight(node) {
const leftChild = node.left;
node.left = leftChild.right;
if (leftChild.right !== null) {
leftChild.right.parent = node;
}
leftChild.parent = node.parent;
if (node.parent === null) {
this.root = leftChild;
}
else if (node === node.parent.right) {
node.parent.right = leftChild;
}
else {
node.parent.left = leftChild;
}
leftChild.right = node;
node.parent = leftChild;
}
fixInsert(node) {
while (node !== this.root && node.parent?.color === Color.RED) {
if (node.parent === node.parent.parent?.left) {
const uncle = node.parent.parent.right;
if (uncle?.color === Color.RED) {
node.parent.color = Color.BLACK;
uncle.color = Color.BLACK;
node.parent.parent.color = Color.RED;
node = node.parent.parent;
}
else {
if (node === node.parent.right) {
node = node.parent;
this.rotateLeft(node);
}
node.parent.color = Color.BLACK;
node.parent.parent.color = Color.RED;
this.rotateRight(node.parent.parent);
}
}
else {
const uncle = node.parent.parent?.left;
if (uncle?.color === Color.RED) {
node.parent.color = Color.BLACK;
uncle.color = Color.BLACK;
node.parent.parent.color = Color.RED;
node = node.parent.parent;
}
else {
if (node === node.parent.left) {
node = node.parent;
this.rotateRight(node);
}
node.parent.color = Color.BLACK;
node.parent.parent.color = Color.RED;
this.rotateLeft(node.parent.parent);
}
}
}
this.root.color = Color.BLACK;
}
insert(key, value) {
const newNode = new RBNode(key, value);
let parent = null;
let current = this.root;
while (current !== null) {
parent = current;
if (this.comparator(key, current.key)) {
current = current.left;
}
else if (this.comparator(current.key, key)) {
current = current.right;
}
else {
// Key already exists, update value
current.value = value;
return;
}
}
newNode.parent = parent;
if (parent === null) {
this.root = newNode;
}
else if (this.comparator(key, parent.key)) {
parent.left = newNode;
}
else {
parent.right = newNode;
}
this.nodeCount++;
this.fixInsert(newNode);
}
findNode(key) {
let current = this.root;
while (current !== null) {
if (this.isEqual(key, current.key)) {
return current;
}
current = this.comparator(key, current.key) ? current.left : current.right;
}
return null;
}
find(key) {
const node = this.findNode(key);
return node ? node.value : undefined;
}
findMinNode(node) {
let current = node;
while (current?.left !== null) {
current = current.left;
}
return current;
}
min() {
if (!this.root)
return undefined;
return this.findMinNode(this.root).value;
}
findMaxNode(node) {
let current = node;
while (current?.right !== null) {
current = current.right;
}
return current;
}
max() {
if (!this.root)
return undefined;
return this.findMaxNode(this.root).value;
}
remove(key) {
const node = this.findNode(key);
if (node) {
this.nodeCount--;
this.deleteNode(node);
}
}
deleteNode(node) {
let x;
let y = node;
let originalColor = y.color;
if (node.left === null) {
x = node.right;
this.transplant(node, node.right);
}
else if (node.right === null) {
x = node.left;
this.transplant(node, node.left);
}
else {
y = this.findMinNode(node.right);
originalColor = y.color;
x = y.right;
if (y.parent === node) {
if (x)
x.parent = y;
}
else {
this.transplant(y, y.right);
y.right = node.right;
y.right.parent = y;
}
this.transplant(node, y);
y.left = node.left;
y.left.parent = y;
y.color = node.color;
}
if (originalColor === Color.BLACK && x) {
this.fixDelete(x);
}
}
transplant(u, v) {
if (u.parent === null) {
this.root = v;
}
else if (u === u.parent.left) {
u.parent.left = v;
}
else {
u.parent.right = v;
}
if (v !== null) {
v.parent = u.parent;
}
}
fixDelete(x) {
while (x !== this.root && x.color === Color.BLACK) {
if (x === x.parent.left) {
let w = x.parent.right;
if (w.color === Color.RED) {
w.color = Color.BLACK;
x.parent.color = Color.RED;
this.rotateLeft(x.parent);
w = x.parent.right;
}
if ((!w.left || w.left.color === Color.BLACK) &&
(!w.right || w.right.color === Color.BLACK)) {
w.color = Color.RED;
x = x.parent;
}
else {
if (!w.right || w.right.color === Color.BLACK) {
if (w.left)
w.left.color = Color.BLACK;
w.color = Color.RED;
this.rotateRight(w);
w = x.parent.right;
}
w.color = x.parent.color;
x.parent.color = Color.BLACK;
if (w.right)
w.right.color = Color.BLACK;
this.rotateLeft(x.parent);
x = this.root;
}
}
else {
let w = x.parent.left;
if (w.color === Color.RED) {
w.color = Color.BLACK;
x.parent.color = Color.RED;
this.rotateRight(x.parent);
w = x.parent.left;
}
if ((!w.right || w.right.color === Color.BLACK) &&
(!w.left || w.left.color === Color.BLACK)) {
w.color = Color.RED;
x = x.parent;
}
else {
if (!w.left || w.left.color === Color.BLACK) {
if (w.right)
w.right.color = Color.BLACK;
w.color = Color.RED;
this.rotateLeft(w);
w = x.parent.left;
}
w.color = x.parent.color;
x.parent.color = Color.BLACK;
if (w.left)
w.left.color = Color.BLACK;
this.rotateRight(x.parent);
x = this.root;
}
}
}
x.color = Color.BLACK;
}
isEmpty() {
return this.root === null;
}
size() {
return this.nodeCount;
}
clear() {
this.root = null;
this.nodeCount = 0;
}
isEqual(a, b) {
// Two values are equal if neither is less than the other
return !this.comparator(a, b) && !this.comparator(b, a);
}
inorderTraversal(node, callback) {
if (node !== null) {
this.inorderTraversal(node.left, callback);
callback(node.key, node.value);
this.inorderTraversal(node.right, callback);
}
}
forEach(callback) {
this.inorderTraversal(this.root, callback);
}
/**
* Checks if two red-black trees are equal.
* Returns false if comparing with null/undefined.
*/
equals(other) {
if (!other || !(other instanceof RedBlackTree))
return false;
if (this.size() !== other.size())
return false;
return this.areTreesEqual(this.root, other.root);
}
areTreesEqual(node1, node2) {
if (node1 === null && node2 === null)
return true;
if (node1 === null || node2 === null)
return false;
if (!this.isEqual(node1.key, node2.key) ||
!utils_1.Utils.equals(node1.value, node2.value))
return false;
return this.areTreesEqual(node1.left, node2.left) && this.areTreesEqual(node1.right, node2.right);
}
}
exports.RedBlackTree = RedBlackTree;
//# sourceMappingURL=red-black-tree.js.map