implemented deletion of elements & and iteration method & lots of tests
This commit is contained in:
		
							parent
							
								
									7b52111c31
								
							
						
					
					
						commit
						fe4564542b
					
				@ -9,7 +9,7 @@ class N {
 | 
				
			|||||||
    this._left = null;
 | 
					    this._left = null;
 | 
				
			||||||
    this._right = null;
 | 
					    this._right = null;
 | 
				
			||||||
    this._parent = null;
 | 
					    this._parent = null;
 | 
				
			||||||
    if (val.id == null) {
 | 
					    if (val.id === null) {
 | 
				
			||||||
      throw new Error("You must define id!");
 | 
					      throw new Error("You must define id!");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -23,6 +23,10 @@ class N {
 | 
				
			|||||||
  get parent () {
 | 
					  get parent () {
 | 
				
			||||||
    return this._parent;
 | 
					    return this._parent;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  get sibling () {
 | 
				
			||||||
 | 
					    return (this === this.parent.left) ?
 | 
				
			||||||
 | 
					            this.parent.right : this.parent.left;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  get left () {
 | 
					  get left () {
 | 
				
			||||||
    return this._left;
 | 
					    return this._left;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -30,13 +34,13 @@ class N {
 | 
				
			|||||||
    return this._right;
 | 
					    return this._right;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  set left (n) {
 | 
					  set left (n) {
 | 
				
			||||||
    if (n != null) {
 | 
					    if (n !== null) {
 | 
				
			||||||
      n._parent = this;
 | 
					      n._parent = this;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    this._left = n;
 | 
					    this._left = n;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  set right (n) {
 | 
					  set right (n) {
 | 
				
			||||||
    if (n != null) {
 | 
					    if (n !== null) {
 | 
				
			||||||
      n._parent = this;
 | 
					      n._parent = this;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    this._right = n;
 | 
					    this._right = n;
 | 
				
			||||||
@ -47,7 +51,7 @@ class N {
 | 
				
			|||||||
    var newRight = this.right.left;
 | 
					    var newRight = this.right.left;
 | 
				
			||||||
    newParent.left = this;
 | 
					    newParent.left = this;
 | 
				
			||||||
    this.right = newRight;
 | 
					    this.right = newRight;
 | 
				
			||||||
    if (parent == null) {
 | 
					    if (parent === null) {
 | 
				
			||||||
      tree.root = newParent;
 | 
					      tree.root = newParent;
 | 
				
			||||||
      newParent._parent = null;
 | 
					      newParent._parent = null;
 | 
				
			||||||
    } else if (parent.left === this) {
 | 
					    } else if (parent.left === this) {
 | 
				
			||||||
@ -58,13 +62,29 @@ class N {
 | 
				
			|||||||
      throw new Error("The elements are wrongly connected!");
 | 
					      throw new Error("The elements are wrongly connected!");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  next () {
 | 
				
			||||||
 | 
					    if ( this.right !== null ) {
 | 
				
			||||||
 | 
					      // search the most left node in the right tree
 | 
				
			||||||
 | 
					      var o = this.right;
 | 
				
			||||||
 | 
					      while (o.left !== null) {
 | 
				
			||||||
 | 
					        o = o.left;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return o;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      var p = this;
 | 
				
			||||||
 | 
					      while (p.parent !== null && p !== p.parent.left) {
 | 
				
			||||||
 | 
					        p = p.parent;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return p.parent;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  rotateRight (tree) {
 | 
					  rotateRight (tree) {
 | 
				
			||||||
    var parent = this.parent;
 | 
					    var parent = this.parent;
 | 
				
			||||||
    var newParent = this.left;
 | 
					    var newParent = this.left;
 | 
				
			||||||
    var newLeft = this.left.right;
 | 
					    var newLeft = this.left.right;
 | 
				
			||||||
    newParent.right = this;
 | 
					    newParent.right = this;
 | 
				
			||||||
    this.left = newLeft;
 | 
					    this.left = newLeft;
 | 
				
			||||||
    if (parent == null) {
 | 
					    if (parent === null) {
 | 
				
			||||||
      tree.root = newParent;
 | 
					      tree.root = newParent;
 | 
				
			||||||
      newParent._parent = null;
 | 
					      newParent._parent = null;
 | 
				
			||||||
    } else if (parent.left === this) {
 | 
					    } else if (parent.left === this) {
 | 
				
			||||||
@ -89,13 +109,49 @@ class RBTree { //eslint-disable-line no-unused-vars
 | 
				
			|||||||
  constructor () {
 | 
					  constructor () {
 | 
				
			||||||
    this.root = null;
 | 
					    this.root = null;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  find (id) {
 | 
					  findNodeWithLowerBound (from) {
 | 
				
			||||||
    var o = this.root;
 | 
					    var o = this.root;
 | 
				
			||||||
    if (o == null) {
 | 
					    if (o === null) {
 | 
				
			||||||
      return false;
 | 
					      return false;
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      while (true) {
 | 
					      while (true) {
 | 
				
			||||||
        if (o == null) {
 | 
					        if ((from === null || from < o.val.id) && o.left !== null) {
 | 
				
			||||||
 | 
					          // o is included in the bound
 | 
				
			||||||
 | 
					          // try to find an element that is closer to the bound
 | 
				
			||||||
 | 
					          o = o.left;
 | 
				
			||||||
 | 
					        } else if (o.val.id < from) {
 | 
				
			||||||
 | 
					          // o is not within the bound, maybe one of the right elements is..
 | 
				
			||||||
 | 
					          if (o.right !== null) {
 | 
				
			||||||
 | 
					            o = o.right;
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            // there is no right element. Search for the next bigger element,
 | 
				
			||||||
 | 
					            // this should be within the bounds
 | 
				
			||||||
 | 
					            return o.next();
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          return o;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  iterate (from, to, f) {
 | 
				
			||||||
 | 
					    var o = this.findNodeWithLowerBound(from);
 | 
				
			||||||
 | 
					    while ( o !== null && (to === null || o.val.id <= to) ) {
 | 
				
			||||||
 | 
					      f(o.val);
 | 
				
			||||||
 | 
					      o = o.next();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  find (id) {
 | 
				
			||||||
 | 
					    return this.findNode(id).val;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  findNode (id) {
 | 
				
			||||||
 | 
					    var o = this.root;
 | 
				
			||||||
 | 
					    if (o === null) {
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      while (true) {
 | 
				
			||||||
 | 
					        if (o === null) {
 | 
				
			||||||
          return false;
 | 
					          return false;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (id < o.val.id) {
 | 
					        if (id < o.val.id) {
 | 
				
			||||||
@ -103,25 +159,160 @@ class RBTree { //eslint-disable-line no-unused-vars
 | 
				
			|||||||
        } else if (o.val.id < id) {
 | 
					        } else if (o.val.id < id) {
 | 
				
			||||||
          o = o.right;
 | 
					          o = o.right;
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
          return o.val;
 | 
					          return o;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  delete (id) {
 | 
				
			||||||
 | 
					    var d = this.findNode(id);
 | 
				
			||||||
 | 
					    if (d.left !== null && d.right !== null) {
 | 
				
			||||||
 | 
					      // switch d with the greates element in the left subtree.
 | 
				
			||||||
 | 
					      // o should have at most one child.
 | 
				
			||||||
 | 
					      var o = d.left;
 | 
				
			||||||
 | 
					      // find
 | 
				
			||||||
 | 
					      while (o.right !== null) {
 | 
				
			||||||
 | 
					        o = o.right;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      // switch
 | 
				
			||||||
 | 
					      d.val = o.val;
 | 
				
			||||||
 | 
					      d = o;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // d has at most one child
 | 
				
			||||||
 | 
					    // let n be the node that replaces d
 | 
				
			||||||
 | 
					    var isFakeChild;
 | 
				
			||||||
 | 
					    var child = d.left || d.right;
 | 
				
			||||||
 | 
					    if ( child === null) {
 | 
				
			||||||
 | 
					      isFakeChild = true;
 | 
				
			||||||
 | 
					      child = new N({id: 0});
 | 
				
			||||||
 | 
					      child.blacken();
 | 
				
			||||||
 | 
					      d.right = child;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      isFakeChild = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (d.parent === null) {
 | 
				
			||||||
 | 
					      if (!isFakeChild) {
 | 
				
			||||||
 | 
					        this.root = child;
 | 
				
			||||||
 | 
					        child.blacken();
 | 
				
			||||||
 | 
					        child._parent = null;
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        this.root = null;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    } else if (d.parent.left === d) {
 | 
				
			||||||
 | 
					      d.parent.left = child;
 | 
				
			||||||
 | 
					    } else if (d.parent.right === d) {
 | 
				
			||||||
 | 
					      d.parent.right = child;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      throw new Error("Impossible!");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if ( d.isBlack() ) {
 | 
				
			||||||
 | 
					      if ( child.isRed() ) {
 | 
				
			||||||
 | 
					        child.blacken();
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        this._fixDelete(child);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    this.root.blacken();
 | 
				
			||||||
 | 
					    if (isFakeChild) {
 | 
				
			||||||
 | 
					      if (child.parent.left === child) {
 | 
				
			||||||
 | 
					        child.parent.left = null;
 | 
				
			||||||
 | 
					      } else if (child.parent.right === child) {
 | 
				
			||||||
 | 
					        child.parent.right = null;
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        throw new Error("Impossible #3");
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  _fixDelete (n) {
 | 
				
			||||||
 | 
					    function isBlack (node) {
 | 
				
			||||||
 | 
					      return node !== null ? node.isBlack() : true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    function isRed(node) {
 | 
				
			||||||
 | 
					      return node !== null ? node.isRed() : false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (n.parent === null) {
 | 
				
			||||||
 | 
					      // this can only be called after the first iteration of fixDelete.
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // d was already replaced by the child
 | 
				
			||||||
 | 
					    // d is not the root
 | 
				
			||||||
 | 
					    // d and child are black
 | 
				
			||||||
 | 
					    var sibling = n.sibling;
 | 
				
			||||||
 | 
					    if (isRed(sibling)) {
 | 
				
			||||||
 | 
					      // make sibling the grandfather
 | 
				
			||||||
 | 
					      n.parent.redden();
 | 
				
			||||||
 | 
					      sibling.blacken();
 | 
				
			||||||
 | 
					      if (n === n.parent.left) {
 | 
				
			||||||
 | 
					        n.parent.rotateLeft(this);
 | 
				
			||||||
 | 
					      } else if (n === n.parent.right) {
 | 
				
			||||||
 | 
					        n.parent.rotateRight(this);
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        throw new Error("Impossible #2");
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      sibling = n.sibling;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // parent, sibling, and children of n are black
 | 
				
			||||||
 | 
					    if ( n.parent.isBlack() &&
 | 
				
			||||||
 | 
					         sibling.isBlack() &&
 | 
				
			||||||
 | 
					         isBlack(sibling.left) &&
 | 
				
			||||||
 | 
					         isBlack(sibling.right)
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					      sibling.redden();
 | 
				
			||||||
 | 
					      this._fixDelete(n.parent);
 | 
				
			||||||
 | 
					    } else if ( n.parent.isRed() &&
 | 
				
			||||||
 | 
					                sibling.isBlack() &&
 | 
				
			||||||
 | 
					                isBlack(sibling.left) &&
 | 
				
			||||||
 | 
					                isBlack(sibling.right)
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					      sibling.redden();
 | 
				
			||||||
 | 
					      n.parent.blacken();
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      if ( n === n.parent.left &&
 | 
				
			||||||
 | 
					           sibling.isBlack() &&
 | 
				
			||||||
 | 
					           isRed(sibling.left) &&
 | 
				
			||||||
 | 
					           isBlack(sibling.right)
 | 
				
			||||||
 | 
					      ) {
 | 
				
			||||||
 | 
					        sibling.redden();
 | 
				
			||||||
 | 
					        sibling.left.blacken();
 | 
				
			||||||
 | 
					        sibling.rotateRight(this);
 | 
				
			||||||
 | 
					        sibling = n.sibling;
 | 
				
			||||||
 | 
					      } else if ( n === n.parent.right &&
 | 
				
			||||||
 | 
					                  sibling.isBlack() &&
 | 
				
			||||||
 | 
					                  isRed(sibling.right) &&
 | 
				
			||||||
 | 
					                  isBlack(sibling.left)
 | 
				
			||||||
 | 
					      ) {
 | 
				
			||||||
 | 
					        sibling.redden();
 | 
				
			||||||
 | 
					        sibling.right.blacken();
 | 
				
			||||||
 | 
					        sibling.rotateLeft(this);
 | 
				
			||||||
 | 
					        sibling = n.sibling;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      sibling.color = n.parent.color;
 | 
				
			||||||
 | 
					      n.parent.blacken();
 | 
				
			||||||
 | 
					      if (n === n.parent.left) {
 | 
				
			||||||
 | 
					        sibling.right.blacken();
 | 
				
			||||||
 | 
					        n.parent.rotateLeft(this);
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        sibling.left.blacken();
 | 
				
			||||||
 | 
					        n.parent.rotateRight(this);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  add (v) {
 | 
					  add (v) {
 | 
				
			||||||
    var node = new N(v);
 | 
					    var node = new N(v);
 | 
				
			||||||
    if (this.root != null) {
 | 
					    if (this.root !== null) {
 | 
				
			||||||
      var p = this.root; // p abbrev. parent
 | 
					      var p = this.root; // p abbrev. parent
 | 
				
			||||||
      while (true) {
 | 
					      while (true) {
 | 
				
			||||||
        if (node.val.id < p.val.id) {
 | 
					        if (node.val.id < p.val.id) {
 | 
				
			||||||
          if (p.left == null) {
 | 
					          if (p.left === null) {
 | 
				
			||||||
            p.left = node;
 | 
					            p.left = node;
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
          } else {
 | 
					          } else {
 | 
				
			||||||
            p = p.left;
 | 
					            p = p.left;
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        } else if (p.val.id < node.val.id) {
 | 
					        } else if (p.val.id < node.val.id) {
 | 
				
			||||||
          if (p.right == null) {
 | 
					          if (p.right === null) {
 | 
				
			||||||
            p.right = node;
 | 
					            p.right = node;
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
          } else {
 | 
					          } else {
 | 
				
			||||||
@ -131,26 +322,26 @@ class RBTree { //eslint-disable-line no-unused-vars
 | 
				
			|||||||
          return false;
 | 
					          return false;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      this.fixInsert(node);
 | 
					      this._fixInsert(node);
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      this.root = node;
 | 
					      this.root = node;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    this.root.blacken();
 | 
					    this.root.blacken();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  fixInsert (n) {
 | 
					  _fixInsert (n) {
 | 
				
			||||||
    if (n.parent == null) {
 | 
					    if (n.parent === null) {
 | 
				
			||||||
      n.blacken();
 | 
					      n.blacken();
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    } else if (n.parent.isBlack()) {
 | 
					    } else if (n.parent.isBlack()) {
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    var uncle = n.getUncle();
 | 
					    var uncle = n.getUncle();
 | 
				
			||||||
    if (uncle != null && uncle.isRed()) {
 | 
					    if (uncle !== null && uncle.isRed()) {
 | 
				
			||||||
      // Note: parend: red, uncle: red
 | 
					      // Note: parent: red, uncle: red
 | 
				
			||||||
      n.parent.blacken();
 | 
					      n.parent.blacken();
 | 
				
			||||||
      uncle.blacken();
 | 
					      uncle.blacken();
 | 
				
			||||||
      n.grandparent.redden();
 | 
					      n.grandparent.redden();
 | 
				
			||||||
      this.fixInsert(n.grandparent);
 | 
					      this._fixInsert(n.grandparent);
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      // Note: parent: red, uncle: black or null
 | 
					      // Note: parent: red, uncle: black or null
 | 
				
			||||||
      // Now we transform the tree in such a way that
 | 
					      // Now we transform the tree in such a way that
 | 
				
			||||||
 | 
				
			|||||||
@ -1,41 +1,9 @@
 | 
				
			|||||||
/* @flow */
 | 
					/* @flow */
 | 
				
			||||||
/*eslint-env browser,jasmine */
 | 
					/*eslint-env browser,jasmine,console */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var numberOfTests = 100000;
 | 
					var numberOfTests = 10009;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe("RedBlack Tree", function(){
 | 
					function itRedNodesDoNotHaveBlackChildren (tree) {
 | 
				
			||||||
  beforeEach(function(){
 | 
					 | 
				
			||||||
    this.tree = new RBTree();
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
  it("can add&retrieve 5 elements", function(){
 | 
					 | 
				
			||||||
    this.tree.add({val: "four", id: 4});
 | 
					 | 
				
			||||||
    this.tree.add({val: "one", id: 1});
 | 
					 | 
				
			||||||
    this.tree.add({val: "three", id: 3});
 | 
					 | 
				
			||||||
    this.tree.add({val: "two", id: 2});
 | 
					 | 
				
			||||||
    this.tree.add({val: "five", id: 5});
 | 
					 | 
				
			||||||
    expect(this.tree.find(1).val).toEqual("one");
 | 
					 | 
				
			||||||
    expect(this.tree.find(2).val).toEqual("two");
 | 
					 | 
				
			||||||
    expect(this.tree.find(3).val).toEqual("three");
 | 
					 | 
				
			||||||
    expect(this.tree.find(4).val).toEqual("four");
 | 
					 | 
				
			||||||
    expect(this.tree.find(5).val).toEqual("five");
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  describe(`After adding ${numberOfTests} random objects`, function () {
 | 
					 | 
				
			||||||
    var elements = [];
 | 
					 | 
				
			||||||
    var tree = new RBTree();
 | 
					 | 
				
			||||||
    for(var i = 0; i < numberOfTests; i++) {
 | 
					 | 
				
			||||||
      var obj = Math.floor(Math.random() * numberOfTests * 10000);
 | 
					 | 
				
			||||||
      elements.push(obj);
 | 
					 | 
				
			||||||
      tree.add({id: obj});
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    it("root node is black", function(){
 | 
					 | 
				
			||||||
      expect(tree.root.isBlack()).toBeTruthy();
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    it("can find every object", function(){
 | 
					 | 
				
			||||||
      for(var id of elements) {
 | 
					 | 
				
			||||||
        expect(tree.find(id).id).toEqual(id);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  it("Red nodes do not have black children", function(){
 | 
					  it("Red nodes do not have black children", function(){
 | 
				
			||||||
    function traverse (n) {
 | 
					    function traverse (n) {
 | 
				
			||||||
      if (n == null) {
 | 
					      if (n == null) {
 | 
				
			||||||
@ -54,6 +22,9 @@ describe("RedBlack Tree", function(){
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    traverse(tree.root);
 | 
					    traverse(tree.root);
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function itBlackHeightOfSubTreesAreEqual (tree){
 | 
				
			||||||
  it("Black-height of sub-trees are equal", function(){
 | 
					  it("Black-height of sub-trees are equal", function(){
 | 
				
			||||||
    function traverse (n) {
 | 
					    function traverse (n) {
 | 
				
			||||||
      if (n == null) {
 | 
					      if (n == null) {
 | 
				
			||||||
@ -70,5 +41,169 @@ describe("RedBlack Tree", function(){
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    traverse(tree.root);
 | 
					    traverse(tree.root);
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function itRootNodeIsBlack(tree) {
 | 
				
			||||||
 | 
					  it("root node is black", function(){
 | 
				
			||||||
 | 
					    expect(tree.root == null || tree.root.isBlack()).toBeTruthy();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe("RedBlack Tree", function(){
 | 
				
			||||||
 | 
					  beforeEach(function(){
 | 
				
			||||||
 | 
					    this.tree = new RBTree();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  it("can add&retrieve 5 elements", function(){
 | 
				
			||||||
 | 
					    this.tree.add({val: "four", id: 4});
 | 
				
			||||||
 | 
					    this.tree.add({val: "one", id: 1});
 | 
				
			||||||
 | 
					    this.tree.add({val: "three", id: 3});
 | 
				
			||||||
 | 
					    this.tree.add({val: "two", id: 2});
 | 
				
			||||||
 | 
					    this.tree.add({val: "five", id: 5});
 | 
				
			||||||
 | 
					    expect(this.tree.find(1).val).toEqual("one");
 | 
				
			||||||
 | 
					    expect(this.tree.find(2).val).toEqual("two");
 | 
				
			||||||
 | 
					    expect(this.tree.find(3).val).toEqual("three");
 | 
				
			||||||
 | 
					    expect(this.tree.find(4).val).toEqual("four");
 | 
				
			||||||
 | 
					    expect(this.tree.find(5).val).toEqual("five");
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it("5 elements do not exist anymore after deleting them", function(){
 | 
				
			||||||
 | 
					    this.tree.add({val: "four", id: 4});
 | 
				
			||||||
 | 
					    this.tree.add({val: "one", id: 1});
 | 
				
			||||||
 | 
					    this.tree.add({val: "three", id: 3});
 | 
				
			||||||
 | 
					    this.tree.add({val: "two", id: 2});
 | 
				
			||||||
 | 
					    this.tree.add({val: "five", id: 5});
 | 
				
			||||||
 | 
					    this.tree.delete(4);
 | 
				
			||||||
 | 
					    expect(this.tree.find(4)).not.toBeTruthy();
 | 
				
			||||||
 | 
					    this.tree.delete(3);
 | 
				
			||||||
 | 
					    expect(this.tree.find(3)).not.toBeTruthy();
 | 
				
			||||||
 | 
					    this.tree.delete(2);
 | 
				
			||||||
 | 
					    expect(this.tree.find(2)).not.toBeTruthy();
 | 
				
			||||||
 | 
					    this.tree.delete(1);
 | 
				
			||||||
 | 
					    expect(this.tree.find(1)).not.toBeTruthy();
 | 
				
			||||||
 | 
					    this.tree.delete(5);
 | 
				
			||||||
 | 
					    expect(this.tree.find(5)).not.toBeTruthy();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it("debug #1", function(){
 | 
				
			||||||
 | 
					    this.tree.add({id: 2});
 | 
				
			||||||
 | 
					    this.tree.add({id: 0});
 | 
				
			||||||
 | 
					    this.tree.delete(2);
 | 
				
			||||||
 | 
					    this.tree.add({id: 1});
 | 
				
			||||||
 | 
					    expect(this.tree.find(0)).not.toBeUndefined();
 | 
				
			||||||
 | 
					    expect(this.tree.find(1)).not.toBeUndefined();
 | 
				
			||||||
 | 
					    expect(this.tree.find(2)).toBeUndefined();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  describe("debug #2", function(){
 | 
				
			||||||
 | 
					    var tree = new RBTree();
 | 
				
			||||||
 | 
					    tree.add({id: 8433});
 | 
				
			||||||
 | 
					    tree.add({id: 12844});
 | 
				
			||||||
 | 
					    tree.add({id: 1795});
 | 
				
			||||||
 | 
					    tree.add({id: 30302});
 | 
				
			||||||
 | 
					    tree.add({id: 64287});
 | 
				
			||||||
 | 
					    tree.delete(8433);
 | 
				
			||||||
 | 
					    tree.add({id: 28996});
 | 
				
			||||||
 | 
					    tree.delete(64287);
 | 
				
			||||||
 | 
					    tree.add({id: 22721});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    itRootNodeIsBlack(tree, []);
 | 
				
			||||||
 | 
					    itBlackHeightOfSubTreesAreEqual(tree, []);
 | 
				
			||||||
 | 
					    itRedNodesDoNotHaveBlackChildren(tree, []);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  describe(`After adding&deleting (0.8/0.2) ${numberOfTests} times`, function () {
 | 
				
			||||||
 | 
					    var elements = [];
 | 
				
			||||||
 | 
					    var tree = new RBTree();
 | 
				
			||||||
 | 
					    for(var i = 0; i < numberOfTests; i++) {
 | 
				
			||||||
 | 
					      var r = Math.random();
 | 
				
			||||||
 | 
					      if (r < 0.8) {
 | 
				
			||||||
 | 
					        var obj = Math.floor(Math.random() * numberOfTests * 10000);
 | 
				
			||||||
 | 
					        elements.push(obj);
 | 
				
			||||||
 | 
					        tree.add({id: obj});
 | 
				
			||||||
 | 
					      } else if (elements.length > 0) {
 | 
				
			||||||
 | 
					        var elemid = Math.floor(Math.random() * elements.length);
 | 
				
			||||||
 | 
					        var elem = elements[elemid];
 | 
				
			||||||
 | 
					        elements = elements.filter(function(e){return e != elem; }); //eslint-disable-line
 | 
				
			||||||
 | 
					        tree.delete(elem);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    itRootNodeIsBlack(tree);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it("can find every object", function(){
 | 
				
			||||||
 | 
					      for(var id of elements) {
 | 
				
			||||||
 | 
					        expect(tree.find(id).id).toEqual(id);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it("can find every object with lower bound search", function(){
 | 
				
			||||||
 | 
					      for(var id of elements) {
 | 
				
			||||||
 | 
					        expect(tree.findNodeWithLowerBound(id).val.id).toEqual(id);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    itRedNodesDoNotHaveBlackChildren(tree);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    itBlackHeightOfSubTreesAreEqual(tree);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it("iterating over a tree with lower bound yields the right amount of results", function(){
 | 
				
			||||||
 | 
					      var lowerBound = elements[Math.floor(Math.random() * elements.length)];
 | 
				
			||||||
 | 
					      var expectedResults = elements.filter(function(e, pos){
 | 
				
			||||||
 | 
					        return e >= lowerBound && elements.indexOf(e) === pos;
 | 
				
			||||||
 | 
					      }).length;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var actualResults = 0;
 | 
				
			||||||
 | 
					      tree.iterate(lowerBound, null, function(val){
 | 
				
			||||||
 | 
					        expect(val).not.toBeUndefined();
 | 
				
			||||||
 | 
					        actualResults++;
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      expect(expectedResults).toEqual(actualResults);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it("iterating over a tree without bounds yield the right amount of results", function(){
 | 
				
			||||||
 | 
					      var lowerBound = null;
 | 
				
			||||||
 | 
					      var expectedResults = elements.filter(function(e, pos){
 | 
				
			||||||
 | 
					        return elements.indexOf(e) === pos;
 | 
				
			||||||
 | 
					      }).length;
 | 
				
			||||||
 | 
					      var actualResults = 0;
 | 
				
			||||||
 | 
					      tree.iterate(lowerBound, null, function(val){
 | 
				
			||||||
 | 
					        expect(val).not.toBeUndefined();
 | 
				
			||||||
 | 
					        actualResults++;
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      expect(expectedResults).toEqual(actualResults);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it("iterating over a tree with upper bound yields the right amount of results", function(){
 | 
				
			||||||
 | 
					      var upperBound = elements[Math.floor(Math.random() * elements.length)];
 | 
				
			||||||
 | 
					      var expectedResults = elements.filter(function(e, pos){
 | 
				
			||||||
 | 
					        return e <= upperBound && elements.indexOf(e) === pos;
 | 
				
			||||||
 | 
					      }).length;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var actualResults = 0;
 | 
				
			||||||
 | 
					      tree.iterate(null, upperBound, function(val){
 | 
				
			||||||
 | 
					        expect(val).not.toBeUndefined();
 | 
				
			||||||
 | 
					        actualResults++;
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      expect(expectedResults).toEqual(actualResults);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it("iterating over a tree with upper and lower bounds yield the right amount of results", function(){
 | 
				
			||||||
 | 
					      var b1 = elements[Math.floor(Math.random() * elements.length)];
 | 
				
			||||||
 | 
					      var b2 = elements[Math.floor(Math.random() * elements.length)];
 | 
				
			||||||
 | 
					      var upperBound, lowerBound;
 | 
				
			||||||
 | 
					      if (b1 < b2) {
 | 
				
			||||||
 | 
					        lowerBound = b1;
 | 
				
			||||||
 | 
					        upperBound = b2;
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        lowerBound = b2;
 | 
				
			||||||
 | 
					        upperBound = b1;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      var expectedResults = elements.filter(function(e, pos){
 | 
				
			||||||
 | 
					        return e >= lowerBound && e <= upperBound && elements.indexOf(e) === pos;
 | 
				
			||||||
 | 
					      }).length;
 | 
				
			||||||
 | 
					      var actualResults = 0;
 | 
				
			||||||
 | 
					      tree.iterate(lowerBound, upperBound, function(val){
 | 
				
			||||||
 | 
					        expect(val).not.toBeUndefined();
 | 
				
			||||||
 | 
					        actualResults++;
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      expect(expectedResults).toEqual(actualResults);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user