二叉搜索树(Binary Search Tree)
大约 6 分钟
二叉搜索树(Binary Search Tree)
在计算机科学中,二叉搜索树(Binary Search Tree,BST),有时也被称为有序或排序二叉树,是一种特殊的容器数据结构,用于在内存中存储“项”(例如数字、名称等)。它们允许快速查找、添加和删除项,并可用于实现动态集合或查找表,通过键(例如通过名称查找人的电话号码)来查找项。
二叉搜索树保持其键的有序性,以便查找和其他操作可以利用二分查找的原理:在树中从根到叶子进行遍历,通过与树节点中存储的键进行比较,并根据比较结果决定是继续在左子树还是右子树中查找。平均而言,这意味着每次比较可以跳过树的大约一半的节点,因此每次查找、插入或删除的时间与存储在树中的项的数量的对数成比例。这比在(未排序的)数组中按键查找项所需的线性时间要好得多,但比哈希表的相应操作要慢。
一个大小为9、深度为3的二叉搜索树,根节点为8。 未绘制叶子节点。

基本操作的伪代码
插入
insert(value)
前置条件:value已通过自定义类型检查,类型为T
后置条件:value已放置在树的正确位置
如果 root = ø
root ← 节点(value)
否则
insertNode(root, value)
结束如果
结束插入
insertNode(current, value)
前置条件:current为起始节点
后置条件:value已放置在树的正确位置
如果 value < current.value
如果 current.left = ø
current.left ← 节点(value)
否则
InsertNode(current.left, value)
结束如果
否则
如果 current.right = ø
current.right ← 节点(value)
否则
InsertNode(current.right, value)
结束如果
结束如果
结束insertNode
查找
contains(root, value)
前置条件:root为树的根节点,value为要查找的值
后置条件:确定value是否被找到
如果 root = ø
返回 false
结束如果
如果 root.value = value
返回 true
否则,如果 value < root.value
返回 contains(root.left, value)
否则
返回 contains(root.right, value)
结束如果
结束contains
删除
remove(value)
前置条件:value为要删除的节点的值,root为BST的根节点,count为BST中的项数
后置条件:如果找到并删除了值为value的节点,则返回true;否则返回false
nodeToRemove ← findNode(value)
如果 nodeToRemove = ø
返回 false
结束如果
parent ← findParent(value)
如果 count = 1
root ← ø
否则,如果 nodeToRemove.left = ø 并且 nodeToRemove.right = ø
如果 nodeToRemove.value < parent.value
parent.left ← nodeToRemove.right
否则
parent.right ← nodeToRemove.right
结束如果
否则,如果 nodeToRemove.left != ø 并且 nodeToRemove.right != ø
next ← nodeToRemove.right
当 next.left != ø
next ← next.left
结束循环
如果 next != nodeToRemove.right
remove(next.value)
nodeToRemove.value ← next.value
否则
nodeToRemove.value ← next.value
nodeToRemove.right ← nodeToRemove.right.right
结束如果
否则
如果 nodeToRemove.left = ø
next ← nodeToRemove.right
否则
next ← nodeToRemove.left
结束如果
如果 root = nodeToRemove
root = next
否则,如果 parent.left = nodeToRemove
parent.left = next
否则,如果 parent.right = nodeToRemove
parent.right = next
结束如果
结束如果
count ← count - 1
返回 true
结束remove
查找节点的父节点
findParent(value, root)
前置条件:value为要查找其父节点的节点的值,root为BST的根节点
且不为ø
后置条件:如果找到value的父节点,则返回对其的引用;否则返回ø
如果 value = root.value
返回 ø
结束如果
如果 value < root.value
如果 root.left = ø
返回 ø
否则,如果 root.left.value = value
返回 root
否则
返回 findParent(value, root.left)
结束如果
否则
如果 root.right = ø
返回 ø
否则,如果 root.right.value = value
返回 root
否则
返回 findParent(value, root.right)
结束如果
结束如果
结束findParent
查找节点
findNode(root, value)
前置条件:value为要查找的节点的值,root为BST的根节点
后置条件:如果找到了值为value的节点,则返回对该节点的引用;否则返回ø
如果 root = ø
返回 ø
结束如果
如果 root.value = value
返回 root
否则,如果 value < root.value
返回 findNode(root.left, value)
否则
返回 findNode(root.right, value)
结束如果
结束findNode
查找最小值
findMin(root)
前置条件:root为BST的根节点
后置条件:定位到BST中的最小值
如果 root.left = ø
返回 root.value
结束如果
findMin(root.left)
结束findMin
查找最大值
findMax(root)
前置条件:root为BST的根节点
后置条件:定位到BST中的最大值
如果 root.right = ø
返回 root.value
结束如果
findMax(root.right)
结束findMax
遍历
中序遍历
inorder(root)
前置条件:root为BST的根节点
后置条件:以中序遍历的顺序访问BST中的节点
如果 root != ø
inorder(root.left)
输出 root.value
inorder(root.right)
结束如果
结束inorder
前序遍历
preorder(root)
前置条件:root为BST的根节点
后置条件:以前序遍历的顺序访问BST中的节点
如果 root != ø
输出 root.value
preorder(root.left)
preorder(root.right)
结束如果
结束preorder
后序遍历
postorder(root)
前置条件:root为BST的根节点
后置条件:以后序遍历的顺序访问BST中的节点
如果 root != ø
postorder(root.left)
postorder(root.right)
输出 root.value
结束如果
结束postorder
完整代码
BinarySearchTreeNode
import BinaryTreeNode from '../BinaryTreeNode';
import Comparator from '../../../utils/comparator/Comparator';
export default class BinarySearchTreeNode extends BinaryTreeNode {
/**
* @param {*} [value] - node value.
* @param {function} [compareFunction] - comparator function for node values.
*/
constructor(value = null, compareFunction = undefined) {
super(value);
// This comparator is used to compare node values with each other.
this.compareFunction = compareFunction;
this.nodeValueComparator = new Comparator(compareFunction);
}
/**
* @param {*} value
* @return {BinarySearchTreeNode}
*/
insert(value) {
if (this.nodeValueComparator.equal(this.value, null)) {
this.value = value;
return this;
}
if (this.nodeValueComparator.lessThan(value, this.value)) {
// Insert to the left.
if (this.left) {
return this.left.insert(value);
}
const newNode = new BinarySearchTreeNode(value, this.compareFunction);
this.setLeft(newNode);
return newNode;
}
if (this.nodeValueComparator.greaterThan(value, this.value)) {
// Insert to the right.
if (this.right) {
return this.right.insert(value);
}
const newNode = new BinarySearchTreeNode(value, this.compareFunction);
this.setRight(newNode);
return newNode;
}
return this;
}
/**
* @param {*} value
* @return {BinarySearchTreeNode}
*/
find(value) {
// Check the root.
if (this.nodeValueComparator.equal(this.value, value)) {
return this;
}
if (this.nodeValueComparator.lessThan(value, this.value) && this.left) {
// Check left nodes.
return this.left.find(value);
}
if (this.nodeValueComparator.greaterThan(value, this.value) && this.right) {
// Check right nodes.
return this.right.find(value);
}
return null;
}
/**
* @param {*} value
* @return {boolean}
*/
contains(value) {
return !!this.find(value);
}
/**
* @param {*} value
* @return {boolean}
*/
remove(value) {
const nodeToRemove = this.find(value);
if (!nodeToRemove) {
throw new Error('Item not found in the tree');
}
const { parent } = nodeToRemove;
if (!nodeToRemove.left && !nodeToRemove.right) {
// Node is a leaf and thus has no children.
if (parent) {
// Node has a parent. Just remove the pointer to this node from the parent.
parent.removeChild(nodeToRemove);
} else {
// Node has no parent. Just erase current node value.
nodeToRemove.setValue(undefined);
}
} else if (nodeToRemove.left && nodeToRemove.right) {
// Node has two children.
// Find the next biggest value (minimum value in the right branch)
// and replace current value node with that next biggest value.
const nextBiggerNode = nodeToRemove.right.findMin();
if (!this.nodeComparator.equal(nextBiggerNode, nodeToRemove.right)) {
this.remove(nextBiggerNode.value);
nodeToRemove.setValue(nextBiggerNode.value);
} else {
// In case if next right value is the next bigger one and it doesn't have left child
// then just replace node that is going to be deleted with the right node.
nodeToRemove.setValue(nodeToRemove.right.value);
nodeToRemove.setRight(nodeToRemove.right.right);
}
} else {
// Node has only one child.
// Make this child to be a direct child of current node's parent.
/** @var BinarySearchTreeNode */
const childNode = nodeToRemove.left || nodeToRemove.right;
if (parent) {
parent.replaceChild(nodeToRemove, childNode);
} else {
BinaryTreeNode.copyNode(childNode, nodeToRemove);
}
}
// Clear the parent of removed node.
nodeToRemove.parent = null;
return true;
}
/**
* @return {BinarySearchTreeNode}
*/
findMin() {
if (!this.left) {
return this;
}
return this.left.findMin();
}
}
BinarySearchTree
import BinarySearchTreeNode from './BinarySearchTreeNode';
export default class BinarySearchTree {
/**
* @param {function} [nodeValueCompareFunction]
*/
constructor(nodeValueCompareFunction) {
this.root = new BinarySearchTreeNode(null, nodeValueCompareFunction);
// Steal node comparator from the root.
this.nodeComparator = this.root.nodeComparator;
}
/**
* @param {*} value
* @return {BinarySearchTreeNode}
*/
insert(value) {
return this.root.insert(value);
}
/**
* @param {*} value
* @return {boolean}
*/
contains(value) {
return this.root.contains(value);
}
/**
* @param {*} value
* @return {boolean}
*/
remove(value) {
return this.root.remove(value);
}
/**
* @return {string}
*/
toString() {
return this.root.toString();
}
}
复杂度
时间复杂度
访问 | 查找 | 插入 | 删除 |
---|---|---|---|
O(log(n)) | O(log(n)) | O(log(n)) | O(log(n)) |
空间复杂度
O(n)