UNPKG

typescript-ds-lib

Version:

A collection of TypeScript data structure implementations

350 lines 11 kB
"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