splay
Version:
Immutable splay trees.
299 lines (261 loc) • 6.54 kB
JavaScript
var not = {
right: 'left',
left: 'right'
}
var empty = new SplayNode()
empty.left = empty.right = empty
SplayNode.prototype.empty = empty
if (typeof Object.freeze === 'function') {
Object.freeze(empty)
}
module.exports = createTree
function createTree (compare) {
if (!compare) return empty
function SpecificSplay (value, left, right) {
this.value = value
this.left = left || root
this.right = right || root
}
SpecificSplay.prototype = new SplayNode()
SpecificSplay.prototype.constructor = SpecificSplay
SpecificSplay.prototype._compare = compare
var root = new SpecificSplay()
root.left = root.right = root
SpecificSplay.prototype.empty = root
if (typeof Object.freeze === 'function') {
Object.freeze(root)
}
return root
}
function SplayNode (value, left, right) {
this.value = value
this.left = left || empty
this.right = right || empty
}
SplayNode.prototype._compare = defCompare
SplayNode.prototype.access = function access (item) {
var path = this._pathTo(item)
return this._splay(path)
}
SplayNode.prototype.find = function find (start, end, reverse) {
if (typeof end === 'undefined') end = start
var results = []
var node = this.access(start)
while (!node.isEmpty() && this._compare(end, node.value) >= 0) {
results.push(node.value)
node = node.right.first()
}
return reverse ? results.reverse() : results
}
SplayNode.prototype._pathTo = function pathTo (item) {
var node = this
var path = []
var side = ''
var comp
path.push(node.copy())
while (!node.isEmpty()) {
comp = this._compare(item, node.value)
if (comp === 0) {
break
}
if (comp < 0) {
side = 'left'
} else {
side = 'right'
}
node = node[side]
if (node.isEmpty()) {
break
}
path.push(side)
path.push(node.copy())
}
return path
}
SplayNode.prototype.insert = function insert (item) {
return this._splay(this._place(new this.constructor(item)))
}
SplayNode.prototype.remove = function remove (item) {
var path = this._pathTo(item)
var len = path.length
var node = path.pop()
var last = node.left.join(node.right)
if (len === 1) return last
return this._splay(this._pluck(item))
}
SplayNode.prototype.forEach = function forEach (cb) {
var idx = 0
var tree = this.first()
while (!tree.isEmpty()) {
cb(tree.value, idx++)
tree = tree.right.first()
}
}
SplayNode.prototype.join = function join (right) {
var left = this
if (left.isEmpty()) {
return right
}
var result = this._splay(left._highest())
result.right = right
return result
}
SplayNode.prototype.split = function (item) {
var more = this.access(item)
var less = more.left
more.left = this.empty
return [ less, more ]
}
SplayNode.prototype.uInsert = function (item) {
if (this.isEmpty()) return this.insert(item)
var root = this.access(item)
if (!root.isEmpty() && this._compare(item, root.value) === 0) {
root.value = item
return root
} else {
return this.insert(item)
}
}
SplayNode.prototype.isEmpty = function isEmpty () {
return (this === this.right && this === this.left)
}
SplayNode.prototype.first = function first () {
if (this.left.isEmpty()) {
return this
}
return this._splay(this._lowest())
}
SplayNode.prototype.last = function last () {
if (this.right.isEmpty()) {
return this
}
return this._splay(this._highest())
}
SplayNode.prototype.unshift = function unshift (item) {
var path = this._lowest()
path.push('left')
path.push(new this.constructor(item))
return this._splay(path)
}
SplayNode.prototype.shift = function shift () {
return this._splay(this._lowest()).right
}
SplayNode.prototype.pop = function pop () {
return this._splay(this._highest()).left
}
SplayNode.prototype.push = function push (item) {
var path = this._highest()
path.push('right')
path.push(new this.constructor(item))
return this._splay(path)
}
SplayNode.prototype._splay = function _splay (path) {
var newRoot = path.pop()
var par
var gp
var tmp
var first
var second
while (true) {
second = path.pop()
par = path.pop()
if (!par) break
first = path.pop()
gp = path.pop()
tmp = newRoot[not[second]]
newRoot[not[second]] = par
par[second] = tmp
if (first === second) {
tmp = par[not[first]]
par[not[first]] = gp
gp[first] = tmp
} else if (first === not[second]) {
tmp = newRoot[not[first]]
newRoot[not[first]] = gp
gp[first] = tmp
}
}
return newRoot
}
SplayNode.prototype._place = function _place (toInsert) {
var path = []
var side = ''
var node = this
while (!node.isEmpty()) {
path.push(node.copy())
if (this._compare(toInsert.value, node.value) < 0) {
side = 'left'
} else {
side = 'right'
}
node = node[side]
path.push(side)
}
path.push(toInsert)
return path
}
SplayNode.prototype._pluck = function _pluck (toDelete) {
var node = this
var path = []
var side = ''
while (!node.isEmpty()) {
path.push(node.copy())
if (this._compare(toDelete, node.value) < 0) {
side = 'left'
} else {
side = 'right'
}
node = node[side]
if (toDelete === node.value) {
break
}
path.push(side)
}
path[path.length - 1][side] = node.left.join(node.right)
return path
}
SplayNode.prototype.copy = function copy () {
return new this.constructor(this.value, this.left, this.right)
}
SplayNode.prototype._higher = function _higher () {
var path = [this.copy(), 'right']
var node = this.right
if (node.isEmpty()) return this
path.push(this.right.copy())
return node._lowest(path)
}
SplayNode.prototype._lower = function _lower () {
var path = [this.copy(), 'left']
var node = this.left
if (node.isEmpty()) return path
return node._highest(path)
}
SplayNode.prototype._highest = function _highest (path) {
var node = this
path || (path = [])
path.push(this.copy())
while (!node.right.isEmpty()) {
node = node.right
path.push('right')
path.push(node.copy())
}
return path
}
SplayNode.prototype._lowest = function _lowest (path) {
path || (path = [ this.copy() ])
var node = path[path.length - 1]
while (!node.left.isEmpty()) {
node = node.left
path.push('left')
path.push(node.copy())
}
return path
}
function defCompare (a, b) {
var stringA = String(a)
var stringB = String(b)
if (stringA === stringB) {
return 0
}
return stringA > stringB ? 1 : -1
}