| "use strict" |
| |
| module.exports = createRBTree |
| |
| var RED = 0 |
| var BLACK = 1 |
| |
| function RBNode(color, key, value, left, right, count) { |
| this._color = color |
| this.key = key |
| this.value = value |
| this.left = left |
| this.right = right |
| this._count = count |
| } |
| |
| function cloneNode(node) { |
| return new RBNode(node._color, node.key, node.value, node.left, node.right, node._count) |
| } |
| |
| function repaint(color, node) { |
| return new RBNode(color, node.key, node.value, node.left, node.right, node._count) |
| } |
| |
| function recount(node) { |
| node._count = 1 + (node.left ? node.left._count : 0) + (node.right ? node.right._count : 0) |
| } |
| |
| function RedBlackTree(compare, root) { |
| this._compare = compare |
| this.root = root |
| } |
| |
| var proto = RedBlackTree.prototype |
| |
| Object.defineProperty(proto, "keys", { |
| get: function() { |
| var result = [] |
| this.forEach(function(k,v) { |
| result.push(k) |
| }) |
| return result |
| } |
| }) |
| |
| Object.defineProperty(proto, "values", { |
| get: function() { |
| var result = [] |
| this.forEach(function(k,v) { |
| result.push(v) |
| }) |
| return result |
| } |
| }) |
| |
| //Returns the number of nodes in the tree |
| Object.defineProperty(proto, "length", { |
| get: function() { |
| if(this.root) { |
| return this.root._count |
| } |
| return 0 |
| } |
| }) |
| |
| //Insert a new item into the tree |
| proto.insert = function(key, value) { |
| var cmp = this._compare |
| //Find point to insert new node at |
| var n = this.root |
| var n_stack = [] |
| var d_stack = [] |
| while(n) { |
| var d = cmp(key, n.key) |
| n_stack.push(n) |
| d_stack.push(d) |
| if(d <= 0) { |
| n = n.left |
| } else { |
| n = n.right |
| } |
| } |
| //Rebuild path to leaf node |
| n_stack.push(new RBNode(RED, key, value, null, null, 1)) |
| for(var s=n_stack.length-2; s>=0; --s) { |
| var n = n_stack[s] |
| if(d_stack[s] <= 0) { |
| n_stack[s] = new RBNode(n._color, n.key, n.value, n_stack[s+1], n.right, n._count+1) |
| } else { |
| n_stack[s] = new RBNode(n._color, n.key, n.value, n.left, n_stack[s+1], n._count+1) |
| } |
| } |
| //Rebalance tree using rotations |
| //console.log("start insert", key, d_stack) |
| for(var s=n_stack.length-1; s>1; --s) { |
| var p = n_stack[s-1] |
| var n = n_stack[s] |
| if(p._color === BLACK || n._color === BLACK) { |
| break |
| } |
| var pp = n_stack[s-2] |
| if(pp.left === p) { |
| if(p.left === n) { |
| var y = pp.right |
| if(y && y._color === RED) { |
| //console.log("LLr") |
| p._color = BLACK |
| pp.right = repaint(BLACK, y) |
| pp._color = RED |
| s -= 1 |
| } else { |
| //console.log("LLb") |
| pp._color = RED |
| pp.left = p.right |
| p._color = BLACK |
| p.right = pp |
| n_stack[s-2] = p |
| n_stack[s-1] = n |
| recount(pp) |
| recount(p) |
| if(s >= 3) { |
| var ppp = n_stack[s-3] |
| if(ppp.left === pp) { |
| ppp.left = p |
| } else { |
| ppp.right = p |
| } |
| } |
| break |
| } |
| } else { |
| var y = pp.right |
| if(y && y._color === RED) { |
| //console.log("LRr") |
| p._color = BLACK |
| pp.right = repaint(BLACK, y) |
| pp._color = RED |
| s -= 1 |
| } else { |
| //console.log("LRb") |
| p.right = n.left |
| pp._color = RED |
| pp.left = n.right |
| n._color = BLACK |
| n.left = p |
| n.right = pp |
| n_stack[s-2] = n |
| n_stack[s-1] = p |
| recount(pp) |
| recount(p) |
| recount(n) |
| if(s >= 3) { |
| var ppp = n_stack[s-3] |
| if(ppp.left === pp) { |
| ppp.left = n |
| } else { |
| ppp.right = n |
| } |
| } |
| break |
| } |
| } |
| } else { |
| if(p.right === n) { |
| var y = pp.left |
| if(y && y._color === RED) { |
| //console.log("RRr", y.key) |
| p._color = BLACK |
| pp.left = repaint(BLACK, y) |
| pp._color = RED |
| s -= 1 |
| } else { |
| //console.log("RRb") |
| pp._color = RED |
| pp.right = p.left |
| p._color = BLACK |
| p.left = pp |
| n_stack[s-2] = p |
| n_stack[s-1] = n |
| recount(pp) |
| recount(p) |
| if(s >= 3) { |
| var ppp = n_stack[s-3] |
| if(ppp.right === pp) { |
| ppp.right = p |
| } else { |
| ppp.left = p |
| } |
| } |
| break |
| } |
| } else { |
| var y = pp.left |
| if(y && y._color === RED) { |
| //console.log("RLr") |
| p._color = BLACK |
| pp.left = repaint(BLACK, y) |
| pp._color = RED |
| s -= 1 |
| } else { |
| //console.log("RLb") |
| p.left = n.right |
| pp._color = RED |
| pp.right = n.left |
| n._color = BLACK |
| n.right = p |
| n.left = pp |
| n_stack[s-2] = n |
| n_stack[s-1] = p |
| recount(pp) |
| recount(p) |
| recount(n) |
| if(s >= 3) { |
| var ppp = n_stack[s-3] |
| if(ppp.right === pp) { |
| ppp.right = n |
| } else { |
| ppp.left = n |
| } |
| } |
| break |
| } |
| } |
| } |
| } |
| //Return new tree |
| n_stack[0]._color = BLACK |
| return new RedBlackTree(cmp, n_stack[0]) |
| } |
| |
| |
| //Visit all nodes inorder |
| function doVisitFull(visit, node) { |
| if(node.left) { |
| var v = doVisitFull(visit, node.left) |
| if(v) { return v } |
| } |
| var v = visit(node.key, node.value) |
| if(v) { return v } |
| if(node.right) { |
| return doVisitFull(visit, node.right) |
| } |
| } |
| |
| //Visit half nodes in order |
| function doVisitHalf(lo, compare, visit, node) { |
| var l = compare(lo, node.key) |
| if(l <= 0) { |
| if(node.left) { |
| var v = doVisitHalf(lo, compare, visit, node.left) |
| if(v) { return v } |
| } |
| var v = visit(node.key, node.value) |
| if(v) { return v } |
| } |
| if(node.right) { |
| return doVisitHalf(lo, compare, visit, node.right) |
| } |
| } |
| |
| //Visit all nodes within a range |
| function doVisit(lo, hi, compare, visit, node) { |
| var l = compare(lo, node.key) |
| var h = compare(hi, node.key) |
| var v |
| if(l <= 0) { |
| if(node.left) { |
| v = doVisit(lo, hi, compare, visit, node.left) |
| if(v) { return v } |
| } |
| if(h > 0) { |
| v = visit(node.key, node.value) |
| if(v) { return v } |
| } |
| } |
| if(h > 0 && node.right) { |
| return doVisit(lo, hi, compare, visit, node.right) |
| } |
| } |
| |
| |
| proto.forEach = function rbTreeForEach(visit, lo, hi) { |
| if(!this.root) { |
| return |
| } |
| switch(arguments.length) { |
| case 1: |
| return doVisitFull(visit, this.root) |
| break |
| |
| case 2: |
| return doVisitHalf(lo, this._compare, visit, this.root) |
| break |
| |
| case 3: |
| if(this._compare(lo, hi) >= 0) { |
| return |
| } |
| return doVisit(lo, hi, this._compare, visit, this.root) |
| break |
| } |
| } |
| |
| //First item in list |
| Object.defineProperty(proto, "begin", { |
| get: function() { |
| var stack = [] |
| var n = this.root |
| while(n) { |
| stack.push(n) |
| n = n.left |
| } |
| return new RedBlackTreeIterator(this, stack) |
| } |
| }) |
| |
| //Last item in list |
| Object.defineProperty(proto, "end", { |
| get: function() { |
| var stack = [] |
| var n = this.root |
| while(n) { |
| stack.push(n) |
| n = n.right |
| } |
| return new RedBlackTreeIterator(this, stack) |
| } |
| }) |
| |
| //Find the ith item in the tree |
| proto.at = function(idx) { |
| if(idx < 0) { |
| return new RedBlackTreeIterator(this, []) |
| } |
| var n = this.root |
| var stack = [] |
| while(true) { |
| stack.push(n) |
| if(n.left) { |
| if(idx < n.left._count) { |
| n = n.left |
| continue |
| } |
| idx -= n.left._count |
| } |
| if(!idx) { |
| return new RedBlackTreeIterator(this, stack) |
| } |
| idx -= 1 |
| if(n.right) { |
| if(idx >= n.right._count) { |
| break |
| } |
| n = n.right |
| } else { |
| break |
| } |
| } |
| return new RedBlackTreeIterator(this, []) |
| } |
| |
| proto.ge = function(key) { |
| var cmp = this._compare |
| var n = this.root |
| var stack = [] |
| var last_ptr = 0 |
| while(n) { |
| var d = cmp(key, n.key) |
| stack.push(n) |
| if(d <= 0) { |
| last_ptr = stack.length |
| } |
| if(d <= 0) { |
| n = n.left |
| } else { |
| n = n.right |
| } |
| } |
| stack.length = last_ptr |
| return new RedBlackTreeIterator(this, stack) |
| } |
| |
| proto.gt = function(key) { |
| var cmp = this._compare |
| var n = this.root |
| var stack = [] |
| var last_ptr = 0 |
| while(n) { |
| var d = cmp(key, n.key) |
| stack.push(n) |
| if(d < 0) { |
| last_ptr = stack.length |
| } |
| if(d < 0) { |
| n = n.left |
| } else { |
| n = n.right |
| } |
| } |
| stack.length = last_ptr |
| return new RedBlackTreeIterator(this, stack) |
| } |
| |
| proto.lt = function(key) { |
| var cmp = this._compare |
| var n = this.root |
| var stack = [] |
| var last_ptr = 0 |
| while(n) { |
| var d = cmp(key, n.key) |
| stack.push(n) |
| if(d > 0) { |
| last_ptr = stack.length |
| } |
| if(d <= 0) { |
| n = n.left |
| } else { |
| n = n.right |
| } |
| } |
| stack.length = last_ptr |
| return new RedBlackTreeIterator(this, stack) |
| } |
| |
| proto.le = function(key) { |
| var cmp = this._compare |
| var n = this.root |
| var stack = [] |
| var last_ptr = 0 |
| while(n) { |
| var d = cmp(key, n.key) |
| stack.push(n) |
| if(d >= 0) { |
| last_ptr = stack.length |
| } |
| if(d < 0) { |
| n = n.left |
| } else { |
| n = n.right |
| } |
| } |
| stack.length = last_ptr |
| return new RedBlackTreeIterator(this, stack) |
| } |
| |
| //Finds the item with key if it exists |
| proto.find = function(key) { |
| var cmp = this._compare |
| var n = this.root |
| var stack = [] |
| while(n) { |
| var d = cmp(key, n.key) |
| stack.push(n) |
| if(d === 0) { |
| return new RedBlackTreeIterator(this, stack) |
| } |
| if(d <= 0) { |
| n = n.left |
| } else { |
| n = n.right |
| } |
| } |
| return new RedBlackTreeIterator(this, []) |
| } |
| |
| //Removes item with key from tree |
| proto.remove = function(key) { |
| var iter = this.find(key) |
| if(iter) { |
| return iter.remove() |
| } |
| return this |
| } |
| |
| //Returns the item at `key` |
| proto.get = function(key) { |
| var cmp = this._compare |
| var n = this.root |
| while(n) { |
| var d = cmp(key, n.key) |
| if(d === 0) { |
| return n.value |
| } |
| if(d <= 0) { |
| n = n.left |
| } else { |
| n = n.right |
| } |
| } |
| return |
| } |
| |
| //Iterator for red black tree |
| function RedBlackTreeIterator(tree, stack) { |
| this.tree = tree |
| this._stack = stack |
| } |
| |
| var iproto = RedBlackTreeIterator.prototype |
| |
| //Test if iterator is valid |
| Object.defineProperty(iproto, "valid", { |
| get: function() { |
| return this._stack.length > 0 |
| } |
| }) |
| |
| //Node of the iterator |
| Object.defineProperty(iproto, "node", { |
| get: function() { |
| if(this._stack.length > 0) { |
| return this._stack[this._stack.length-1] |
| } |
| return null |
| }, |
| enumerable: true |
| }) |
| |
| //Makes a copy of an iterator |
| iproto.clone = function() { |
| return new RedBlackTreeIterator(this.tree, this._stack.slice()) |
| } |
| |
| //Swaps two nodes |
| function swapNode(n, v) { |
| n.key = v.key |
| n.value = v.value |
| n.left = v.left |
| n.right = v.right |
| n._color = v._color |
| n._count = v._count |
| } |
| |
| //Fix up a double black node in a tree |
| function fixDoubleBlack(stack) { |
| var n, p, s, z |
| for(var i=stack.length-1; i>=0; --i) { |
| n = stack[i] |
| if(i === 0) { |
| n._color = BLACK |
| return |
| } |
| //console.log("visit node:", n.key, i, stack[i].key, stack[i-1].key) |
| p = stack[i-1] |
| if(p.left === n) { |
| //console.log("left child") |
| s = p.right |
| if(s.right && s.right._color === RED) { |
| //console.log("case 1: right sibling child red") |
| s = p.right = cloneNode(s) |
| z = s.right = cloneNode(s.right) |
| p.right = s.left |
| s.left = p |
| s.right = z |
| s._color = p._color |
| n._color = BLACK |
| p._color = BLACK |
| z._color = BLACK |
| recount(p) |
| recount(s) |
| if(i > 1) { |
| var pp = stack[i-2] |
| if(pp.left === p) { |
| pp.left = s |
| } else { |
| pp.right = s |
| } |
| } |
| stack[i-1] = s |
| return |
| } else if(s.left && s.left._color === RED) { |
| //console.log("case 1: left sibling child red") |
| s = p.right = cloneNode(s) |
| z = s.left = cloneNode(s.left) |
| p.right = z.left |
| s.left = z.right |
| z.left = p |
| z.right = s |
| z._color = p._color |
| p._color = BLACK |
| s._color = BLACK |
| n._color = BLACK |
| recount(p) |
| recount(s) |
| recount(z) |
| if(i > 1) { |
| var pp = stack[i-2] |
| if(pp.left === p) { |
| pp.left = z |
| } else { |
| pp.right = z |
| } |
| } |
| stack[i-1] = z |
| return |
| } |
| if(s._color === BLACK) { |
| if(p._color === RED) { |
| //console.log("case 2: black sibling, red parent", p.right.value) |
| p._color = BLACK |
| p.right = repaint(RED, s) |
| return |
| } else { |
| //console.log("case 2: black sibling, black parent", p.right.value) |
| p.right = repaint(RED, s) |
| continue |
| } |
| } else { |
| //console.log("case 3: red sibling") |
| s = cloneNode(s) |
| p.right = s.left |
| s.left = p |
| s._color = p._color |
| p._color = RED |
| recount(p) |
| recount(s) |
| if(i > 1) { |
| var pp = stack[i-2] |
| if(pp.left === p) { |
| pp.left = s |
| } else { |
| pp.right = s |
| } |
| } |
| stack[i-1] = s |
| stack[i] = p |
| if(i+1 < stack.length) { |
| stack[i+1] = n |
| } else { |
| stack.push(n) |
| } |
| i = i+2 |
| } |
| } else { |
| //console.log("right child") |
| s = p.left |
| if(s.left && s.left._color === RED) { |
| //console.log("case 1: left sibling child red", p.value, p._color) |
| s = p.left = cloneNode(s) |
| z = s.left = cloneNode(s.left) |
| p.left = s.right |
| s.right = p |
| s.left = z |
| s._color = p._color |
| n._color = BLACK |
| p._color = BLACK |
| z._color = BLACK |
| recount(p) |
| recount(s) |
| if(i > 1) { |
| var pp = stack[i-2] |
| if(pp.right === p) { |
| pp.right = s |
| } else { |
| pp.left = s |
| } |
| } |
| stack[i-1] = s |
| return |
| } else if(s.right && s.right._color === RED) { |
| //console.log("case 1: right sibling child red") |
| s = p.left = cloneNode(s) |
| z = s.right = cloneNode(s.right) |
| p.left = z.right |
| s.right = z.left |
| z.right = p |
| z.left = s |
| z._color = p._color |
| p._color = BLACK |
| s._color = BLACK |
| n._color = BLACK |
| recount(p) |
| recount(s) |
| recount(z) |
| if(i > 1) { |
| var pp = stack[i-2] |
| if(pp.right === p) { |
| pp.right = z |
| } else { |
| pp.left = z |
| } |
| } |
| stack[i-1] = z |
| return |
| } |
| if(s._color === BLACK) { |
| if(p._color === RED) { |
| //console.log("case 2: black sibling, red parent") |
| p._color = BLACK |
| p.left = repaint(RED, s) |
| return |
| } else { |
| //console.log("case 2: black sibling, black parent") |
| p.left = repaint(RED, s) |
| continue |
| } |
| } else { |
| //console.log("case 3: red sibling") |
| s = cloneNode(s) |
| p.left = s.right |
| s.right = p |
| s._color = p._color |
| p._color = RED |
| recount(p) |
| recount(s) |
| if(i > 1) { |
| var pp = stack[i-2] |
| if(pp.right === p) { |
| pp.right = s |
| } else { |
| pp.left = s |
| } |
| } |
| stack[i-1] = s |
| stack[i] = p |
| if(i+1 < stack.length) { |
| stack[i+1] = n |
| } else { |
| stack.push(n) |
| } |
| i = i+2 |
| } |
| } |
| } |
| } |
| |
| //Removes item at iterator from tree |
| iproto.remove = function() { |
| var stack = this._stack |
| if(stack.length === 0) { |
| return this.tree |
| } |
| //First copy path to node |
| var cstack = new Array(stack.length) |
| var n = stack[stack.length-1] |
| cstack[cstack.length-1] = new RBNode(n._color, n.key, n.value, n.left, n.right, n._count) |
| for(var i=stack.length-2; i>=0; --i) { |
| var n = stack[i] |
| if(n.left === stack[i+1]) { |
| cstack[i] = new RBNode(n._color, n.key, n.value, cstack[i+1], n.right, n._count) |
| } else { |
| cstack[i] = new RBNode(n._color, n.key, n.value, n.left, cstack[i+1], n._count) |
| } |
| } |
| |
| //Get node |
| n = cstack[cstack.length-1] |
| //console.log("start remove: ", n.value) |
| |
| //If not leaf, then swap with previous node |
| if(n.left && n.right) { |
| //console.log("moving to leaf") |
| |
| //First walk to previous leaf |
| var split = cstack.length |
| n = n.left |
| while(n.right) { |
| cstack.push(n) |
| n = n.right |
| } |
| //Copy path to leaf |
| var v = cstack[split-1] |
| cstack.push(new RBNode(n._color, v.key, v.value, n.left, n.right, n._count)) |
| cstack[split-1].key = n.key |
| cstack[split-1].value = n.value |
| |
| //Fix up stack |
| for(var i=cstack.length-2; i>=split; --i) { |
| n = cstack[i] |
| cstack[i] = new RBNode(n._color, n.key, n.value, n.left, cstack[i+1], n._count) |
| } |
| cstack[split-1].left = cstack[split] |
| } |
| //console.log("stack=", cstack.map(function(v) { return v.value })) |
| |
| //Remove leaf node |
| n = cstack[cstack.length-1] |
| if(n._color === RED) { |
| //Easy case: removing red leaf |
| //console.log("RED leaf") |
| var p = cstack[cstack.length-2] |
| if(p.left === n) { |
| p.left = null |
| } else if(p.right === n) { |
| p.right = null |
| } |
| cstack.pop() |
| for(var i=0; i<cstack.length; ++i) { |
| cstack[i]._count-- |
| } |
| return new RedBlackTree(this.tree._compare, cstack[0]) |
| } else { |
| if(n.left || n.right) { |
| //Second easy case: Single child black parent |
| //console.log("BLACK single child") |
| if(n.left) { |
| swapNode(n, n.left) |
| } else if(n.right) { |
| swapNode(n, n.right) |
| } |
| //Child must be red, so repaint it black to balance color |
| n._color = BLACK |
| for(var i=0; i<cstack.length-1; ++i) { |
| cstack[i]._count-- |
| } |
| return new RedBlackTree(this.tree._compare, cstack[0]) |
| } else if(cstack.length === 1) { |
| //Third easy case: root |
| //console.log("ROOT") |
| return new RedBlackTree(this.tree._compare, null) |
| } else { |
| //Hard case: Repaint n, and then do some nasty stuff |
| //console.log("BLACK leaf no children") |
| for(var i=0; i<cstack.length; ++i) { |
| cstack[i]._count-- |
| } |
| var parent = cstack[cstack.length-2] |
| fixDoubleBlack(cstack) |
| //Fix up links |
| if(parent.left === n) { |
| parent.left = null |
| } else { |
| parent.right = null |
| } |
| } |
| } |
| return new RedBlackTree(this.tree._compare, cstack[0]) |
| } |
| |
| //Returns key |
| Object.defineProperty(iproto, "key", { |
| get: function() { |
| if(this._stack.length > 0) { |
| return this._stack[this._stack.length-1].key |
| } |
| return |
| }, |
| enumerable: true |
| }) |
| |
| //Returns value |
| Object.defineProperty(iproto, "value", { |
| get: function() { |
| if(this._stack.length > 0) { |
| return this._stack[this._stack.length-1].value |
| } |
| return |
| }, |
| enumerable: true |
| }) |
| |
| |
| //Returns the position of this iterator in the sorted list |
| Object.defineProperty(iproto, "index", { |
| get: function() { |
| var idx = 0 |
| var stack = this._stack |
| if(stack.length === 0) { |
| var r = this.tree.root |
| if(r) { |
| return r._count |
| } |
| return 0 |
| } else if(stack[stack.length-1].left) { |
| idx = stack[stack.length-1].left._count |
| } |
| for(var s=stack.length-2; s>=0; --s) { |
| if(stack[s+1] === stack[s].right) { |
| ++idx |
| if(stack[s].left) { |
| idx += stack[s].left._count |
| } |
| } |
| } |
| return idx |
| }, |
| enumerable: true |
| }) |
| |
| //Advances iterator to next element in list |
| iproto.next = function() { |
| var stack = this._stack |
| if(stack.length === 0) { |
| return |
| } |
| var n = stack[stack.length-1] |
| if(n.right) { |
| n = n.right |
| while(n) { |
| stack.push(n) |
| n = n.left |
| } |
| } else { |
| stack.pop() |
| while(stack.length > 0 && stack[stack.length-1].right === n) { |
| n = stack[stack.length-1] |
| stack.pop() |
| } |
| } |
| } |
| |
| //Checks if iterator is at end of tree |
| Object.defineProperty(iproto, "hasNext", { |
| get: function() { |
| var stack = this._stack |
| if(stack.length === 0) { |
| return false |
| } |
| if(stack[stack.length-1].right) { |
| return true |
| } |
| for(var s=stack.length-1; s>0; --s) { |
| if(stack[s-1].left === stack[s]) { |
| return true |
| } |
| } |
| return false |
| } |
| }) |
| |
| //Update value |
| iproto.update = function(value) { |
| var stack = this._stack |
| if(stack.length === 0) { |
| throw new Error("Can't update empty node!") |
| } |
| var cstack = new Array(stack.length) |
| var n = stack[stack.length-1] |
| cstack[cstack.length-1] = new RBNode(n._color, n.key, value, n.left, n.right, n._count) |
| for(var i=stack.length-2; i>=0; --i) { |
| n = stack[i] |
| if(n.left === stack[i+1]) { |
| cstack[i] = new RBNode(n._color, n.key, n.value, cstack[i+1], n.right, n._count) |
| } else { |
| cstack[i] = new RBNode(n._color, n.key, n.value, n.left, cstack[i+1], n._count) |
| } |
| } |
| return new RedBlackTree(this.tree._compare, cstack[0]) |
| } |
| |
| //Moves iterator backward one element |
| iproto.prev = function() { |
| var stack = this._stack |
| if(stack.length === 0) { |
| return |
| } |
| var n = stack[stack.length-1] |
| if(n.left) { |
| n = n.left |
| while(n) { |
| stack.push(n) |
| n = n.right |
| } |
| } else { |
| stack.pop() |
| while(stack.length > 0 && stack[stack.length-1].left === n) { |
| n = stack[stack.length-1] |
| stack.pop() |
| } |
| } |
| } |
| |
| //Checks if iterator is at start of tree |
| Object.defineProperty(iproto, "hasPrev", { |
| get: function() { |
| var stack = this._stack |
| if(stack.length === 0) { |
| return false |
| } |
| if(stack[stack.length-1].left) { |
| return true |
| } |
| for(var s=stack.length-1; s>0; --s) { |
| if(stack[s-1].right === stack[s]) { |
| return true |
| } |
| } |
| return false |
| } |
| }) |
| |
| //Default comparison function |
| function defaultCompare(a, b) { |
| if(a < b) { |
| return -1 |
| } |
| if(a > b) { |
| return 1 |
| } |
| return 0 |
| } |
| |
| //Build a tree |
| function createRBTree(compare) { |
| return new RedBlackTree(compare || defaultCompare, null) |
| } |