使用JS实现二叉搜索树、平衡二叉树、红黑树(待实现)、线段树(待实现)。
1-二叉树节点
// ./binary-tree-node.js
module.exports = class BinaryTreeNode {
/**
*
* @param {*} data
* @param {function(d1: *, d2: *)} [compare]
*/
constructor(data, compare) {
this.data = data || null
this.left = null
this.right = null
this.parent = null
this.compare = compare || BinaryTreeNode.defaultCompare
}
/**
*
* @param {*} d1
* @param {*} d2
* @returns { number }
*/
static defaultCompare(d1, d2) {
if (d1 === d2) return 0
return d1 > d2 ? 1 : -1
}
get leftHeight() {
if (!this.left) return 0
return this.left.height
}
get rightHeight() {
if (!this.right) return 0
return this.right.height
}
get height() {
return Math.max(this.leftHeight, this.rightHeight) + 1
}
get grandpa() {
if (!this.parent || this.parent.parent) return null
return this.parent.parent
}
// for red-black tree to balance
get uncle() {
if (!this.grandpa) return null
if (!this.grandpa.left || !this.grandpa.right) return null
if (Object.is(this.parent, this.grandpa.left)) return this.grandpa.right
return this.grandpa.left
}
// 修改
setData(data) {
this.data = data
return this
}
setLeftChild(node) {
this.left = node
if (this.left) {
this.left.parent = this
}
return this
}
setRightChild(node) {
this.right = node
if (this.right) {
this.right.parent = this
}
return this
}
static assign(target, source) {
target.setData(source.data)
target.setLeftChild(source.left)
target.setRightChild(source.right)
target.parent = source.parent
return target
}
// 中序遍历
traverseInorder() {
let traverse = []
if (this.left) {
traverse = [...traverse, ...this.left.traverseInorder()]
}
traverse.push(this.data)
if (this.right) {
traverse = [...traverse, ...this.right.traverseInorder()]
}
return traverse
}
}
2-搜索二叉树节点
// ./binary-search-tree-node.js
const BinaryTreeNode = require("./binary-tree-node")
class BinarySearchTreeNode extends BinaryTreeNode {
constructor(value, compare) {
super(value, compare)
}
get balanceFactor() {
return this.leftHeight - this.rightHeight
}
// find inorder precessor of root in left subtree
findPrecessor() {
if (!this.right) return this
return this.right.findPrecessor()
}
// 查找
find(data) {
if (this.compare(this.data, data) === 0) return this
if (this.compare(this.data, data) < 0 && this.right) return this.right.find(data)
if (this.compare(this.data, data) > 0 && this.left) return this.left.find(data)
return null
}
contains(data) {
return !!this.find(data)
}
// 新增
append(data) {
if (Object.is(this.data, null)) {
return this.setData(data)
}
// append to right
if (this.compare(this.data, data) <= 0) {
if (this.right) {
return this.right.append(data)
}
const newNode = new BinarySearchTreeNode(data)
this.setRightChild(newNode)
return newNode
}
// append to left
if (this.left) {
return this.left.append(data)
}
const newNode = new BinarySearchTreeNode(data)
this.setLeftChild(newNode)
return newNode
}
// 删除
remove(data) {
const nodeToRemove = this.find(data)
if (!nodeToRemove) {
throw new Error(`remove error: node with ${data} is not found in the tree`);
}
const { parent } = nodeToRemove
// nodeToRemove without children
if (!nodeToRemove.left && !nodeToRemove.right) {
if (parent) {
if (this.compare(parent.data, nodeToRemove.data) <= 0) {
parent.setRightChild(null)
} else {
parent.setLeftChild(null)
}
return nodeToRemove
} else {
// for root
return nodeToRemove.setData(null)
}
}
// 2) nodeToRemove with two children
if (nodeToRemove.left && nodeToRemove.right) {
const nodeToRemoveCopy = new BinarySearchTreeNode()
BinaryTreeNode.assign(nodeToRemoveCopy, nodeToRemove)
// find the in order predessor of nodeToRemove
const predessor = nodeToRemove.left.findPrecessor()
if (predessor === nodeToRemove.left) {
nodeToRemove.setData(nodeToRemove.left.data)
nodeToRemove.setLeftChild(nodeToRemove.left.left)
} else {
// remove predessor
nodeToRemove.setData(predessor.data)
predessor.parent.setRightChild(null)
}
return nodeToRemoveCopy
}
// 3) nodeToRemove with one child
const child = nodeToRemove.left || nodeToRemove.right
// If it has parent, just set parent point to child of nodeToRemove
if (parent) {
if (parent.compare(parent.data, nodeToRemove.data) <= 0) {
parent.setRightChild(child)
} else {
parent.setLeftChild(child)
}
return nodeToRemove
}
// If no parent, replace nodeToRemove data with it child data
const nodeToRemoveCopy = new BinarySearchTreeNode()
BinaryTreeNode.assign(nodeToRemoveCopy, nodeToRemove)
nodeToRemove.setData(child.data)
nodeToRemove.setLeftChild(null)
nodeToRemove.setRightChild(null)
return nodeToRemoveCopy
}
}
3-搜索二叉树
// ./binary-search-tree.js
const BinarySearchTreeNode = require("./binary-search-tree-node")
class BinarySearchTree {
constructor(compare) {
this.root = new BinarySearchTreeNode(null, compare)
this.compare = compare
}
append(data) {
return this.root.append(data)
}
remove(data) {
return this.root.remove(data)
}
find(data) {
return this.root.find(data)
}
contains(data) {
return this.root.contains(data)
}
traverse() {
return this.root.traverseInorder()
}
}
module.exports = BinarySearchTree
// **************TEST***********************
const list = [-20, 6, 20, 10, -10, 25, 15]
const tree = new BinarySearchTree()
// test appendlist.forEach((v) => { tree.append(v) })
// test traverse
console.log(tree.traverse());
// test find and contains
console.log(tree.contains(10));
// test remove
// 1) without children
console.log(tree.remove(10));
// 2) with one child
console.log(tree.remove(15));
// 3) with two children
console.log(tree.remove(20));
// 4) not found
console.log(tree.remove(1));
4-平衡二叉树
// ./avl-tree.js
const BinarySearchTree = require("./binary-search-tree")
class AVLTree extends BinarySearchTree {
// right child with right subtree
leftRotate(rootNode) {
const rightNode = rootNode.right
rootNode.setRightChild(rightNode.left)
if (rootNode.parent) {
rightNode.parent = rootNode.parent
if (this.compare(rootNode.parent.data, rootNode.data) <= 0) {
rightNode.parent.setRightChild(rightNode)
} else {
rightNode.parent.setLeftChild(rightNode)
}
} else {
this.root = rightNode
rightNode.parent = null
}
rightNode.setLeftChild(rootNode)
}
// left child with left subtree,
rightRotate(rootNode) {
const leftNode = rootNode.left
rootNode.setLeftChild(leftNode.right)
if (rootNode.parent) {
leftNode.parent = rootNode.parent
if (this.compare(rootNode.data, rootNode.parent.data) <= 0) {
leftNode.parent.setRightChild(leftNode)
} else {
leftNode.parent.setLeftChild(leftNode)
}
} else {
this.root = leftNode
leftNode.parent = null
}
leftNode.setRightChild(rootNode)
}
// left child with right subtree
leftRightRotate(rootNode) {
this.leftRotate(rootNode.left)
this.rightRotate(rootNode)
}
// right child with left subtree
rightLeftRotate(rootNode) {
this.rightRotate(rootNode.right)
this.leftRotate(rootNode)
}
checkBalance(node) {
// when node is parent of root, just return
if(!node) return
if (Math.abs(node.balanceFactor) > 1) {
this.rebalance(node)
}
if (node.parent) {
this.checkBalance(node.parent)
}
}
rebalance(node) {
// imbalance in left
if (node.balanceFactor > 1) {
//left subtree
if (node.left.balanceFactor > 0) {
this.rightRotate(node)
} else {
// right subtree
this.leftRightRotate(node)
}
} else {
//left subtree
if (node.right.balanceFactor > 0) {
this.rightLeftRotate(node)
} else {
// right subtree
this.leftRotate(node)
}
}
}
append(value) {
const appendedNode = super.append(value)
this.checkBalance(appendedNode)
}
remove(value) {
const removedNode = super.remove(value)
this.checkBalance(removedNode.parent)
}
module.exports = AVLTree
// **************TEST***********************
const list = [-20, 6, 20, 10, -10, 25, 15]
const tree = new AVLTree()
// test append
list.forEach((v) => { tree.append(v) })
// test traverse
console.log(tree.traverse());
// test remove
// 1) without children
console.log(tree.remove(15));
// 2) with one child
console.log(tree.remove(10));
// 3) with two children
console.log(tree.remove(20));
// 4) not found
console.log(tree.remove(1));