|
|
"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) }
|