二叉查找树 (Binary Search Tree) 是按照平衡顺序排列的二叉树, 也称二叉搜索树、 有序二叉树(ordered binary tree),排序二叉树(sorted binary tree)。
首先, 要符合二叉树的特性:
平衡排序, 每个节点都有一个 key, 并且每个节点的 key 都符合:
二叉查找树节点必须包含四个字段:
Key
和一个 Value
;对应的 C# 代码实现如下:
class Node {
public TKey Key;
public TValue Val;
public Node Left, Right;
}
上面的代码中, TKey
和 TValue
是泛型类型, TKey
必须实现 IComparable<TKey>
接口, 用于比较两个 TKey
实例的大小。
二叉查找树相比于其他数据结构的优势在于查找、插入的时间复杂度较低。为O(log n)
。
二叉查找树是基础性数据结构,用于构建更为抽象的数据结构,如集合、multiset、关联数
组等。
二叉查找树必须引用根节点, 定义如下:
public class BST<TKey, TValue> where TKey : IComparable<TKey> {
private Node root;
}
既然是二叉查找树, 查找操作肯定要先实现了, 二叉查找树查找的思路是:
对应的 C# 代码实现如下:
public TValue Get(TKey key) {
return Get(root, key);
}
private TValue Get(Node x, TKey key) {
if (x == null) {
return default(TValue);
}
int cmp = key.CompareTo(x.Key);
if (cmp < 0) {
return Get(x.Left, key);
}
if (cmp > 0) {
return Get(x.Right, key);
}
return x.Val;
}
添加操作与查找操作类似, 如果能够在树中找到 key 对应节点, 则设置节点的值, 如果 找不到, 则返回一个新的节点, 实现代码如下:
public void Put(TKey key, TValue val) {
root = Put(root, key, val);
}
private Node Put(Node x, TKey key, TValue val) {
if (x == null) {
return new Node {
Key = key, Val = val, N = 1
};
}
int cmp = key.CompareTo(x.Key);
if (cmp < 0) {
x.Left = Put(x.Left, key, val);
}
else if (cmp > 0) {
x.Right = Put(x.Right, key, val);
}
else {
x.Val = val;
}
x.N = 1 + Size(x.Left) + Size(x.Right);
return x;
}
从二叉查找树中删除指定的节点稍微复杂一点, 要分下面三种情况:
1 删除最小 Key 节点
要删除二叉查找树的最小 key 节点:
对应的 C# 代码实现代码如下:
public void DeleteMin() {
root = DeleteMin(root);
}
private Node DeleteMin(Node x) {
if (x.Left == null) {
return x.Right;
}
x.Left = DeleteMin(x.Left);
x.N = Size(x.Left) + Size(x.Right) + 1;
return x;
}
2 删除最大 key 节点
删除最大 key 节点的思路与删除最小 key 节点的思路类似, 查找节点的右节点即可, 对 应的 C# 实现代码如下:
public void DeleteMax() {
root = DeleteMax(root);
}
private Node DeleteMax(Node x) {
if (x.Right == null) {
return x.Left;
}
x.Right = DeleteMax(x.Right);
x.N = Size(x.Left) + Size(x.Left) + 1;
return x;
}
3 删除任意 key 节点
要从二叉查找树中删除 key 为 k
的节点, 假设树中找到的节点为 t
, 要分下面几
种情况:
如果节点 t
没有子节点, 将节点 t
的父节点指向 t
的引用设置为空即可;
节点 t
的右节点或左节点为空, 则用 t
的另一个节点替换掉 t
即可;
节点 t
的左右节点均不为空, 则需要从 t
的右节点开始找到并删除最小的节点 x
,
并用节点 x
替换 t
的位置;
实现代码如下:
public void Delete(TKey key) {
root = Delete(root, key);
}
private Node Delete(Node x, TKey key) {
if (x == null) {
return null;
}
int cmp = key.CompareTo(x.Key);
if (cmp < 0) {
x.Left = Delete(x.Left, key);
}
else if (cmp > 0) {
x.Right = Delete(x.Right, key);
}
else {
if (x.Right == null) {
return x.Left;
}
if (x.Left == null) {
return x.Right;
}
Node t = x;
x = Min(t.Right);
x.Right = DeleteMin(t.Right);
x.Left = t.Left;
}
x.N = Size(x.Left) + Size(x.Right) + 1;
return x;
}
二叉查找树最终可能的形状如下图所示:
在实际算法中, 应避免最差情况, 因为在这种情况下, 二叉树退化成链表, 查找操作的
速度由 O(LogN)
降为 O(N)
就完全没有意义了。