2018-08-16 22:30:59 +00:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2010-2018 Tim Düsterhus.
|
|
|
|
*
|
|
|
|
* Use of this software is governed by the Business Source License
|
|
|
|
* included in the LICENSE file.
|
|
|
|
*
|
2020-11-19 23:08:57 +00:00
|
|
|
* Change Date: 2024-11-20
|
2018-08-16 22:30:59 +00:00
|
|
|
*
|
|
|
|
* On the date above, in accordance with the Business Source
|
|
|
|
* License, use of this software will be governed by version 2
|
|
|
|
* or later of the General Public License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
define([], function () {
|
|
|
|
'use strict'
|
|
|
|
|
|
|
|
class Node {
|
|
|
|
constructor(value) {
|
|
|
|
this.value = value
|
|
|
|
this._left = undefined
|
|
|
|
this._right = undefined
|
|
|
|
this.parent = undefined
|
|
|
|
this.color = 'RED'
|
|
|
|
}
|
|
|
|
|
|
|
|
get left() {
|
|
|
|
return this._left
|
|
|
|
}
|
|
|
|
|
|
|
|
set left(node) {
|
|
|
|
if (this._left) this._left.parent = undefined
|
|
|
|
if (node !== undefined) {
|
|
|
|
if (node.parent !== undefined) {
|
|
|
|
if (node.isLeftChild) node.parent.left = undefined
|
|
|
|
else if (node.isRightChild) node.parent.right = undefined
|
|
|
|
else throw new Error('Unreachable')
|
|
|
|
}
|
|
|
|
node.parent = this
|
|
|
|
}
|
|
|
|
|
|
|
|
this._left = node
|
|
|
|
}
|
|
|
|
|
|
|
|
get right() {
|
|
|
|
return this._right
|
|
|
|
}
|
|
|
|
|
|
|
|
set right(node) {
|
|
|
|
if (this._right) this._right.parent = undefined
|
|
|
|
if (node !== undefined) {
|
|
|
|
if (node.parent !== undefined) {
|
|
|
|
if (node.isLeftChild) node.parent.left = undefined
|
|
|
|
else if (node.isRightChild) node.parent.right = undefined
|
|
|
|
else throw new Error('Unreachable')
|
|
|
|
}
|
|
|
|
node.parent = this
|
|
|
|
}
|
|
|
|
this._right = node
|
|
|
|
}
|
|
|
|
|
|
|
|
get isRoot() {
|
|
|
|
return this.parent === undefined
|
|
|
|
}
|
|
|
|
|
|
|
|
get isLeaf() {
|
|
|
|
return this.left === undefined && this.right === undefined
|
|
|
|
}
|
|
|
|
|
|
|
|
get isLeftChild() {
|
|
|
|
if (this.parent === undefined) return false
|
|
|
|
return this.parent.left === this
|
|
|
|
}
|
|
|
|
|
|
|
|
get isRightChild() {
|
|
|
|
if (this.parent === undefined) return false
|
|
|
|
return this.parent.right === this
|
|
|
|
}
|
|
|
|
|
|
|
|
get grandparent() {
|
|
|
|
if (this.parent === undefined) return undefined
|
|
|
|
return this.parent.parent
|
|
|
|
}
|
|
|
|
|
|
|
|
get sibling() {
|
|
|
|
if (this.parent === undefined) return undefined
|
|
|
|
if (this.isLeftChild) return this.parent.right
|
|
|
|
return this.parent.left
|
|
|
|
}
|
|
|
|
|
|
|
|
get uncle() {
|
|
|
|
if (this.parent === undefined) return undefined
|
|
|
|
return this.parent.sibling
|
|
|
|
}
|
|
|
|
|
|
|
|
search(value) {
|
|
|
|
if (value === this.value) return ['IS', this]
|
|
|
|
if (value < this.value) {
|
|
|
|
if (this.left !== undefined) return this.left.search(value)
|
|
|
|
return ['LEFT', this]
|
|
|
|
}
|
|
|
|
if (value > this.value) {
|
|
|
|
if (this.right !== undefined) return this.right.search(value)
|
|
|
|
return ['RIGHT', this]
|
|
|
|
}
|
|
|
|
throw new Error('Unreachable')
|
|
|
|
}
|
|
|
|
|
|
|
|
print(depth = 0) {
|
|
|
|
console.log(
|
|
|
|
' '.repeat(depth) +
|
|
|
|
`${this.value}: ${this.color} (Parent: ${
|
|
|
|
this.parent ? this.parent.value : '-'
|
|
|
|
})`
|
|
|
|
)
|
|
|
|
if (this.left) this.left.print(depth + 1)
|
|
|
|
else console.log(' '.repeat(depth + 1) + '-')
|
|
|
|
if (this.right) this.right.print(depth + 1)
|
|
|
|
else console.log(' '.repeat(depth + 1) + '-')
|
|
|
|
}
|
|
|
|
|
|
|
|
check() {
|
|
|
|
if (this.left && this.left.value >= this.value)
|
|
|
|
throw new Error('Invalid' + this.value)
|
|
|
|
if (this.right && this.right.value <= this.value)
|
|
|
|
throw new Error('Invalid' + this.value)
|
|
|
|
if (
|
|
|
|
this.color === 'RED' &&
|
|
|
|
((this.left && this.left.color !== 'BLACK') ||
|
|
|
|
(this.right && this.right.color !== 'BLACK'))
|
2020-11-01 16:41:19 +00:00
|
|
|
)
|
2018-08-16 22:30:59 +00:00
|
|
|
throw new Error('Invalid' + this.value)
|
2020-11-01 16:41:19 +00:00
|
|
|
|
2018-08-16 22:30:59 +00:00
|
|
|
let leftBlacks = 1,
|
|
|
|
rightBlacks = 1
|
|
|
|
if (this.left) {
|
|
|
|
leftBlacks = this.left.check()
|
|
|
|
}
|
|
|
|
if (this.right) {
|
|
|
|
rightBlacks = this.right.check()
|
|
|
|
}
|
|
|
|
if (leftBlacks !== rightBlacks) throw new Error('Invalid' + this.value)
|
|
|
|
|
|
|
|
if (this.color === 'BLACK') return leftBlacks + 1
|
|
|
|
return leftBlacks
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return Node
|
|
|
|
})
|