/** * Red-Black tree is a data structure which is * a type of self-balancing binary search tree. * * @example * * var RBTree = require('../src/data-structures/red-black-tree').RBTree; * var rbTree = new RBTree(); * * rbTree.put(1981, { * name: 'John', * surname: 'Smith' * }); * rbTree.put(2000, { * name: 'Pavlo', * surname: 'Popov' * }); * rbTree.put(1989, { * name: 'Garry', * surname: 'Fisher' * }); * rbTree.put(1990, { * name: 'Derek', * surname: 'Anderson' * }); * * console.log(rbTree.get(1989)); // { name: 'Garry', surname: 'Fisher' } * * @module data-structures/red-black-tree */ (function (exports) { 'use strict'; /** * Enum for the different colors */ var Colors = { RED: 0, BLACK: 1 }; exports.Colors = Colors; /** * Node of the Red-Black tree. * * @private * @constructor * @param {Number} key Key of the node. * @param {Object} value Value assigned to the node. * @param {Node} left Left node. * @param {Node} right Right node. * @param {Number} color Node color. */ function Node(key, value, left, right, color) { this._key = key; this._left = left; this._right = right; this._value = value; this._color = color; } /** * Check or node is red. * * @private * @method * @return {Boolean} Returns true if node is red. */ Node.prototype.isRed = function () { return this._color === Colors.RED; }; /** * Changes node color. * * @private * @method */ Node.prototype.flipColor = function () { if (this._color === Colors.RED) { this._color = Colors.BLACK; } else { this._color = Colors.RED; } }; /** * Creates getters and setters for the properties: * key, value, left, right and color. */ 'key value left right color' .split(' ') .forEach(function (key) { var valueName = key.substr(0, 1).toUpperCase() + key.substr(1, key.length); Node.prototype['get' + valueName] = function () { return this['_' + key]; }; Node.prototype['set' + valueName] = function (val) { this['_' + key] = val; }; }); exports.Node = Node; /** * Red-Black Tree. * * @public * @constructor */ exports.RBTree = function () { this._root = null; }; /** * Add value associated with a given key.

* Complexity: O(log N). * * @public * @method * @param {Number} key Key. * @param {Object} value Value. */ exports.RBTree.prototype.put = function (key, value) { this._root = this._put(key, value, this._root); this._root.setColor(Colors.BLACK); }; /** * Returns true or false depending on whether * given node is red. * * @private * @method * @param {Node} node Node which sould be checked. * @return Returns true if node is red. */ exports.RBTree.prototype.isRed = function (node) { if (!node) { return false; } return node.isRed(); }; /** * Helper function for insertion of given key, * value pair into the Red-Black tree. * * @private * @method * @param {Number} key Key. * @param {Object} value Value. * @param {Node} node Node. */ exports.RBTree.prototype._put = function (key, value, node) { var newRoot = node; if (node === null) { return new Node(key, value, null, null, Colors.RED); } if (node.getKey() > key) { node.setLeft(this._put(key, value, node.getLeft())); } else if (node.getKey() < key) { node.setRight(this._put(key, value, node.getRight())); } else { node.setValue(value); } if (this.isRed(node.getRight()) && !this.isRed(node.getLeft())) { newRoot = this._rotateLeft(node); } if (this.isRed(node.getLeft()) && this.isRed(node.getLeft().getLeft())) { newRoot = this._rotateRight(node); } if (this.isRed(node.getLeft()) && this.isRed(node.getRight())) { this._flipColors(node); } return newRoot; }; /** * Flip the colors of the both neighbours of given node.

* Complexity: O(1). * * @private * @method * @param {Node} node Node. */ exports.RBTree.prototype._flipColors = function (node) { node.getLeft().flipColor(); node.getRight().flipColor(); }; /* * Rotates given node to the left.

* Complexity: O(1). * * @private * @method * @param {Node} node Node. * @return {Node} Right node. */ exports.RBTree.prototype._rotateLeft = function (node) { var x = node.getRight(); if (x !== null) { var temp = x.getLeft(); node.setRight(temp); x.setLeft(node); x.setColor(node.getColor()); node.setColor(Colors.RED); } return x; }; /* * Rotates given node to the right.

* Complexity: O(1). * * @private * @method * @param {Node} node Node. * @return {Node} Left node. */ exports.RBTree.prototype._rotateRight = function (node) { var x = node.getLeft(); if (x !== null) { var temp = x.getRight(); node.setLeft(temp); x.setRight(node); x.setColor(node.getColor()); node.setColor(Colors.RED); } return x; }; /** * Get value by the given key.

* Complexity: O(log N). * * @public * @param {Number} key A key to be searched for. * @return {Object} A value which will be returned based on the key. */ exports.RBTree.prototype.get = function (key) { return this._get(this._root, key); }; /** * Get value by the given key.

* * @private * @param {Node} node Node to start with. * @param {Number} key A key to be searched for. * @return {Object} A value which will be returned based on the key. */ exports.RBTree.prototype._get = function (node, key) { if (node === null) { return undefined; } if (node.getKey() === key) { return node.getValue(); } if (node.getKey() > key) { return this._get(node.getLeft(), key); } else { return this._get(node.getRight(), key); } }; })(typeof window === 'undefined' ? module.exports : window);