270 lines
6.0 KiB
JavaScript
270 lines
6.0 KiB
JavaScript
|
/**
|
||
|
* 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.<br><br>
|
||
|
* 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.<br><br>
|
||
|
* 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.<br><br>
|
||
|
* 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.<br><br>
|
||
|
* 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.<br><br>
|
||
|
* 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.<br><br>
|
||
|
*
|
||
|
* @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);
|