UNPKG

flatten-red-black-tree

Version:
543 lines (470 loc) 22.1 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>index.js - Documentation</title> <script src="scripts/prettify/prettify.js"></script> <script src="scripts/prettify/lang-css.js"></script> <!--[if lt IE 9]> <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> <link type="text/css" rel="stylesheet" href="https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css"> <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> </head> <body> <input type="checkbox" id="nav-trigger" class="nav-trigger" /> <label for="nav-trigger" class="navicon-button x"> <div class="navicon"></div> </label> <label for="nav-trigger" class="overlay"></label> <nav> <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="module.exports-RedBlackTree.html">RedBlackTree</a><ul class='methods'><li data-type='method'><a href="module.exports-RedBlackTree.html#find">find</a></li><li data-type='method'><a href="module.exports-RedBlackTree.html#forEach">forEach</a></li><li data-type='method'><a href="module.exports-RedBlackTree.html#insert">insert</a></li><li data-type='method'><a href="module.exports-RedBlackTree.html#remove">remove</a></li></ul></li></ul> </nav> <div id="main"> <h1 class="page-title">index.js</h1> <section> <article> <pre class="prettyprint source linenums"><code>/** * Created by Alex Bol on 3/27/2017. */ 'use strict'; let {RB_TREE_COLOR_RED, RB_TREE_COLOR_BLACK} = require('./utils/constants'); module.exports = function(traits) { let Node = require('./classes/node')(traits); let nil_node = new Node(); /** * Class implementing red-black tree * @type {RedBlackTree} */ let RedBlackTree = class RedBlackTree { /** * Construct new instance of red-black tree */ constructor() { this.root = null; } /** * Returns size of the tree * @returns {number} */ get size() { let count = 0; this.tree_walk( this.root, () => count++ ); return count; } /** * Returns arrays of keys stored in the tree * @returns {Array} */ get keys() { let res = []; this.tree_walk( this.root, (node) => res.push(node.item.key) ); return res; } /** * Returns true if tree is empty * @returns {boolean} */ get isEmpty() { return (this.root == null || this.root == nil_node); } /** * Insert entry {key[,value]} into the red-black tree * @param {*} key - key object, fit to traits interface * @param {*} value - (optional) value object * @returns {Node} - reference to inserted node */ insert(key, value=key) { if (key === undefined) return; let insert_node = new Node(key, value, nil_node, nil_node, nil_node, RB_TREE_COLOR_RED); this.tree_insert(insert_node); return insert_node; } /** * Find value stored in the tree by given key. Returns value is key found, * otherwise returns undefined * @param {*} key - key object * @param {*} value * @returns {value|undefined} */ find(key, value) { let search_node = new Node(key, value, nil_node, nil_node, nil_node); let found_node = this.tree_search(this.root, search_node); return found_node ? found_node.item.value : undefined; } /** * Remove entry {key,value} stored in the tree * @param key * @param value * @returns {boolean} */ remove(key, value=key) { let search_node = new Node(key, value, nil_node, nil_node, nil_node); let delete_node = this.tree_search(this.root, search_node); if (delete_node) { this.tree_delete(delete_node); return true; } else { return false; } } /** * Iterate through entries stored in the tree * @param {function} visitor */ forEach(visitor) { this.tree_walk( this.root, (node) => visitor(node.item.key, node.item.value) ); }; static tree_insert(insert_node) { var current_node = this.root; var parent_node = null; if (this.root == null || this.root == nil_node) { this.root = insert_node; } else { while (current_node != nil_node) { parent_node = current_node; if (insert_node.less_than(current_node)) { current_node = current_node.left; } else { current_node = current_node.right; } } insert_node.parent = parent_node; if (insert_node.less_than(parent_node)) { parent_node.left = insert_node; } else { parent_node.right = insert_node; } } this.insert_fixup(insert_node); } // After insertion insert_node may have red-colored parent, and this is a single possible violation // Go upwords to the root and re-color until violation will be resolved insert_fixup(insert_node) { let current_node; let uncle_node; current_node = insert_node; while (current_node != this.root &amp;&amp; current_node.parent.color == RB_TREE_COLOR_RED) { if (current_node.parent == current_node.parent.parent.left) { // parent is left child of grandfather uncle_node = current_node.parent.parent.right; // right brother of parent if (uncle_node.color == RB_TREE_COLOR_RED) { // Case 1. Uncle is red // re-color father and uncle into black current_node.parent.color = RB_TREE_COLOR_BLACK; uncle_node.color = RB_TREE_COLOR_BLACK; current_node.parent.parent.color = RB_TREE_COLOR_RED; current_node = current_node.parent.parent; } else { // Case 2 &amp; 3. Uncle is black if (current_node == current_node.parent.right) { // Case 2. Current if right child // This case is transformed into Case 3. current_node = current_node.parent; this.rotate_left(current_node); } current_node.parent.color = RB_TREE_COLOR_BLACK; // Case 3. Current is left child. // Re-color father and grandfather, rotate grandfather right current_node.parent.parent.color = RB_TREE_COLOR_RED; this.rotate_right(current_node.parent.parent); } } else { // parent is right child of grandfather uncle_node = current_node.parent.parent.left; // left brother of parent if (uncle_node.color == RB_TREE_COLOR_RED) { // Case 4. Uncle is red // re-color father and uncle into black current_node.parent.color = RB_TREE_COLOR_BLACK; uncle_node.color = RB_TREE_COLOR_BLACK; current_node.parent.parent.color = RB_TREE_COLOR_RED; current_node = current_node.parent.parent; } else { if (current_node == current_node.parent.left) { // Case 5. Current is left child // Transform into case 6 current_node = current_node.parent; this.rotate_right(current_node); } current_node.parent.color = RB_TREE_COLOR_BLACK; // Case 6. Current is right child. // Re-color father and grandfather, rotate grandfather left current_node.parent.parent.color = RB_TREE_COLOR_RED; this.rotate_left(current_node.parent.parent); } } } this.root.color = RB_TREE_COLOR_BLACK; } tree_delete(delete_node) { let cut_node; // node to be cut - either delete_node or successor_node ("y" from 14.4) let fix_node; // node to fix rb tree property ("x" from 14.4) if (delete_node.left == nil_node || delete_node.right == nil_node) { // delete_node has less then 2 children cut_node = delete_node; } else { // delete_node has 2 children cut_node = this.tree_successor(delete_node); } // fix_node if single child of cut_node if (cut_node.left != nil_node) { fix_node = cut_node.left; } else { fix_node = cut_node.right; } // remove cut_node from parent if (fix_node != nil_node) { fix_node.parent = cut_node.parent; } if (cut_node == this.root) { this.root = fix_node; } else { if (cut_node == cut_node.parent.left) { cut_node.parent.left = fix_node; } else { cut_node.parent.right = fix_node; } } // COPY DATA !!! // Delete_node becomes cut_node, it means that we cannot hold reference // to node in outer structure and we will have to delete by key, additional search need if (cut_node != delete_node) { delete_node.copy_data(cut_node); } if (fix_node != nil_node &amp;&amp; cut_node.color == RB_TREE_COLOR_BLACK) { this.delete_fixup(fix_node); } } delete_fixup(fix_node) { let current_node = fix_node; let brother_node; while (current_node != this.root &amp;&amp; current_node.parent != null &amp;&amp; current_node.color == RB_TREE_COLOR_BLACK) { if (current_node == current_node.parent.left) { // fix node is left child brother_node = current_node.parent.right; if (brother_node.color == RB_TREE_COLOR_RED) { // Case 1. Brother is red brother_node.color = RB_TREE_COLOR_BLACK; // re-color brother current_node.parent.color = RB_TREE_COLOR_RED; // re-color father this.rotate_left(current_node.parent); brother_node = current_node.parent.right; // update brother } // Derive to cases 2..4: brother is black if (brother_node.left.color == RB_TREE_COLOR_BLACK &amp;&amp; brother_node.right.color == RB_TREE_COLOR_BLACK ) { // case 2: both nephews black brother_node.color = RB_TREE_COLOR_RED; // re-color brother current_node = current_node.parent; // continue iteration } else { if (brother_node.right.color == RB_TREE_COLOR_BLACK) { // case 3: left nephew red, right nephew black brother_node.color = RB_TREE_COLOR_RED; // re-color brother brother_node.left.color = RB_TREE_COLOR_BLACK; // re-color nephew this.rotate_right(brother_node); brother_node = current_node.parent.right; // update brother // Derive to case 4: left nephew black, right nephew red } // case 4: left nephew black, right nephew red brother_node.color = current_node.parent.color; current_node.parent.color = RB_TREE_COLOR_BLACK; brother_node.right.color = RB_TREE_COLOR_BLACK; this.rotate_left(current_node.parent); current_node = this.root; // exit from loop } } else { // fix node is right child brother_node = current_node.parent.left; if (brother_node.color == RB_TREE_COLOR_RED) { // Case 1. Brother is red brother_node.color = RB_TREE_COLOR_BLACK; // re-color brother current_node.parent.color = RB_TREE_COLOR_RED; // re-color father this.rotate_right(current_node.parent); brother_node = current_node.parent.left; // update brother } // Go to cases 2..4 if (brother_node.left.color == RB_TREE_COLOR_BLACK &amp;&amp; brother_node.right.color == RB_TREE_COLOR_BLACK) { // case 2 brother_node.color = RB_TREE_COLOR_RED; // re-color brother current_node = current_node.parent; // continue iteration } else { if (brother_node.left.color == RB_TREE_COLOR_BLACK) { // case 3: right nephew red, left nephew black brother_node.color = RB_TREE_COLOR_RED; // re-color brother brother_node.right.color = RB_TREE_COLOR_BLACK; // re-color nephew this.rotate_left(brother_node); brother_node = current_node.parent.left; // update brother // Derive to case 4: right nephew black, left nephew red } // case 4: right nephew black, left nephew red brother_node.color = current_node.parent.color; current_node.parent.color = RB_TREE_COLOR_BLACK; brother_node.left.color = RB_TREE_COLOR_BLACK; this.rotate_right(current_node.parent); current_node = this.root; // force exit from loop } } } current_node.color = RB_TREE_COLOR_BLACK; } tree_search(node, search_node) { if (node == null || node == nil_node) return undefined; if (search_node.equal_to(node)) { return node; } if (search_node.less_than(node)) { return this.tree_search(node.left, search_node); } else { return this.tree_search(node.right, search_node); } } local_minimum(node) { let node_min = node; while (node_min.left != null &amp;&amp; node_min.left != nil_node) { node_min = node_min.left; } return node_min; } // not in use local_maximum(node) { var node_max = node; while (node_max.right != null &amp;&amp; node_max.right != nil_node) { node_max = node_max.right; } return node_max; } tree_successor(node) { let node_successor; let current_node; let parent_node; if (node.right != nil_node) { node_successor = this.local_minimum(node.right); } else { current_node = node; parent_node = node.parent; while (parent_node != null &amp;&amp; parent_node.right == current_node) { current_node = parent_node; parent_node = parent_node.parent; } node_successor = parent_node; } return node_successor; } // | right-rotate(T,y) | // y ---------------. x // / \ / \ // x c left-rotate(T,x) a y // / \ &lt;--------------- / \ // a b b c rotate_left(x) { //noinspection JSSuspiciousNameCombination let y = x.right; x.right = y.left; // b goes to x.right if (y.left != nil_node) { y.left.parent = x; // x becomes parent of b } y.parent = x.parent; // move parent if (x == this.root) { this.root = y; // y becomes root } else { // y becomes child of x.parent if (x == x.parent.left) { x.parent.left = y; } else { x.parent.right = y; } } y.left = x; // x becomes left child of y x.parent = y; // and y becomes parent of x } // | right-rotate(T,y) | // y ---------------. x // / \ / \ // x c left-rotate(T,x) a y // / \ &lt;--------------- / \ // a b b c rotate_right(y) { var x = y.left; y.left = x.right; // b goes to y.left if (x.right != nil_node) { x.right.parent = y; // y becomes parent of b } x.parent = y.parent; // move parent if (y == this.root) { // x becomes root this.root = x; } else { // y becomes child of x.parent if (y == y.parent.left) { y.parent.left = x; } else { y.parent.right = x; } } x.right = y; // y becomes right child of x y.parent = x; // and x becomes parent of y } tree_walk(node, action) { if (node != null &amp;&amp; node != nil_node) { this.tree_walk(node.left, action); // arr.push(node.toArray()); action(node); this.tree_walk(node.right, action); } } /* Return true if all red nodes have exactly two black child nodes */ testRedBlackProperty() { var res = true; this.tree_walk(this.root, function(node) { if (node.color == RB_TREE_COLOR_RED) { if (!(node.left.color == RB_TREE_COLOR_BLACK &amp;&amp; node.right.color == RB_TREE_COLOR_BLACK)) { res = false; } } }); return res; } testBlackHeightProperty() { [heightLeft, heightRight] = this.blackHeight(this.root); return heightLeft == heightRight; } blackHeight(node) { let height = 0; let heightLeft = 0; let heightRight = 0; if (node.color == RB_TREE_COLOR_BLACK) { height++; } if (node.left != nil_node) { heightLeft = this.blackHeight(node.left); } else { heightLeft = 1; } if (node.right != nil_node) { heightRight = this.blackHeight(node.right); } else { heightRight = 1; } // if (heightLeft != heightRight) { // alert('YOU HAVE A PROBLEM!'); // } height += heightLeft; return height ; } }; return RedBlackTree; }; </code></pre> </article> </section> </div> <br class="clear"> <footer> Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.4.3</a> on Fri Mar 31 2017 20:18:17 GMT+0300 (Jerusalem Daylight Time) using the Minami theme. </footer> <script>prettyPrint();</script> <script src="scripts/linenumber.js"></script> </body> </html>