二叉搜索树
了解二叉搜索树前提要了解 二叉树 二叉树概念
二叉搜索树(BST)又称二叉查找树 或 二叉排序树
二叉搜索树特征如下:
1.二叉搜索树首先是一颗二叉树
2.每个节点的值大于其左子树上所有节点的值(如果左子树不为空)
3.每个节点的值小于其右子树上所有节点的值(如果右子树不为空)
4.所有左子树和右子树自身也必须是二叉搜索树
如下图
当把二叉树所有节点从左到右依次映射到一个序列中,所得到的序列即为一个按照升序排列的序列。
可以使用此方法来验证一颗二叉树,是否是一个二叉搜索树
上图中所有节点从左到有映射到序列中就是
1、2、3、4、5、6、7、8、9
二叉搜索树有:查询、插入、删除 三种操作
查询:
根据二叉树的特性,从根节点开始查询 数值 number
1.令根节点为当前节点 currentNode
2.如果 currentNode 为空 结束返回查询失败
如果 number < currentNode 节点值,则令 currentNode = currentNode.leftChild 循环步骤2
如果 number > currentNode 节点值,则令 currentNode = currentNode.rightChild 循环步骤2
如果 number = currentNode 节点值,则查询完成,返回节点
插入:
1.执行查询,如果已经存在,则结束,返回
2.如果根节点为空,实例化根节点,将 number 赋值给根节点,插入成功返回
3.令根节点为当前节点 currentNode
4.
(1) 如果 number < currentNode 节点值
如果currentNode 有左子树,令 currentNode = currentNode.leftChild 循环步骤 4
如果currentNode 没有左子树,实例化一个节点,节点赋值nunmber,然后令新节点 = currentNode.leftChild 插入完成返回
(2)如果 number > currentNode 节点值
如果currentNode.rightChild 有右子树,令currentNode = currentNode.rightChild 循环步骤4
如果currentNode.rightChild 没有右子树,实例化一个节点,节点赋值number,然后令新节点=currentNode.rightChild 插入完成返回
删除:
1.执行查询,如果不存在,则结束,返回
2.查询到节点 Node, 删除 Node 分三种情况
然后分三种情况
第 (2.1) 种:Node 只有右子树,Node可以直接删除(直接使用Node右子树替换Node),跳转到第 3 步(Node,Node右子树)
第 (2.2) 种:Node 只有左子树,Node可以直接删除(直接使用Node左子树替换Node),跳转到第 3 步(Node,Node左子树)
第 (2.3) 种:Node有左子树和右子树
然后有两种方式选择
第 (2.3.1) 获取 Node 左子树中最大的节点 也叫直接后继节点
第 (2.3.2) 获取 Node 右子树中最小的节点 也叫直接后继节点
(2.3.1) 和 (2.3.2) 任选其一 获取一个即可下面使用 nextNode 代替
如果 nextNode 是 Node 左子树最大的节点,则 nextNode 没有右子树了(因为右子树比它的值更大,右子树应该是最大的节点)
如果 nextNode 是 Node 右子树最小的节点,则 nextNode 没有左子树了(因为左子树比它的值更小,左子树应该是最小的节点)
所以 nextNode 最多只能有一个节点
(2.3.3) 令 Node 的值 = nextNode 的值
(2.3.4) 然后用 nextNode 唯一的子节点 替换 nextNode (到此 nextNode 也就此被删除了) 跳转到第 3 步(nextNode ,nextNode唯一的子节点)
3.(A,B)使用节点 B 替换节点 A
(3.1)如果 A 是根节点,Root = B,Root.parent = null 替换完成,结束
(3.2)获取 parent = A.parent
如果 A 是 parent 的右子树, parent右子树=B
如果 A 是 parent 的左子树, parent左子树=B
如果 B 非空,B.parent = parent
二叉搜索树,查询、插入、删除操作过程,每执行一次,范围减少一半所以二叉搜索树查询、插入、删除的时间复杂度均为 O(logN)
二叉搜索树 C#实现
代码中关于 BinNode 部分是在我 二叉树的文章讲解中定义的
二叉搜索树是继承自 二叉树的,所以 缺少代码请去 我另一篇文章 二叉树中查找
/// <summary>
/// 二叉搜索树
/// </summary>
public class BSTree<T> : BinTree<T> where T : IComparable<T>
{
protected BinNode<T> _hot; // search() 最后访问的非空节点位置
public BSTree()
{
}
/// <summary>
/// 插入
/// </summary>
/// <param name="t"></param>
/// <returns></returns>
public virtual BinNode<T> Insert(T t)
{
BinNode<T> node = Search(t);
if (null != node)
{
return node;
}
node = Insert(t, _hot);
UpdateHeightAbove(node);
return node;
}
/// <summary>
/// 插入,仅内部调用
/// </summary>
/// <param name="t"></param>
/// <param name="parent"></param>
/// <returns></returns>
protected BinNode<T> Insert(T t, BinNode<T> parent)
{
if (null == parent)
{
Root = new BinNode<T>(t);
return Root;
}
else
{
BinNode<T> node = (t.CompareTo(parent.Element) > 0) ? parent.InsertAsRc(t) : parent.InsertAsLc(t);
return node;
}
}
/// <summary>
/// 删除
/// </summary>
public virtual bool Remove(T t)
{
BinNode<T> node = Search(t);
if (null == node)
{
return false;
}
Remove(node, ref _hot);
UpdateHeightAbove(_hot);
return true;
}
/// <summary>
/// 删除,仅内部调用
/// </summary>
/// <param name="node"></param>
/// <param name="hot"></param>
protected BinNode<T> Remove(BinNode<T> node, ref BinNode<T> hot)
{
BinNode<T> succ = null;
if (!node.HasLChild()) // 如果节点没有左孩子,则直接以其右孩子代替
{
succ = Replace(node, node.RightChild); // 令node的右孩子替换node
hot = node.ParentNode;
}
else if (!node.HasRChild()) // 如果节点没有右孩子,则直接以其左孩子代替
{
succ = node.LeftChild;
succ = Replace(node, node.LeftChild); // 令 node 的左孩子替换node
hot = node.ParentNode;
}
else
{
// 要删除节点的直接后继节点
BinNode<T> w = NodeSucc(node);
// 令要删除节点的值等于其直接后继节点的值,然后将直接后继节点删除
node.Element = w.Element;
BinNode<T> parent = w.ParentNode;
// 下面是删除直接后继节点逻辑
// 如果 w 是 左子树最大的节点,则 w 没有右子树了(因为右子树比它的值更大,右子树应该是最大的节点)
// 如果 w 是 右子树最小的节点,则 w 没有左子树了(因为左子树比它的值更小,左子树应该是最小的节点)
// 所以 w 最多只能有一个节点
// 如果 w 是 parent左子树,令 parent左子树 = w 唯一的子节点
// 如果 w 是 parent右子树,令 parent右子树 = w 唯一的子节点
BinNode<T> child = w.HasLChild() ? w.LeftChild : w.RightChild;
if (w.IsLChild())
{
parent.InsertAsLc(child);
}
else
{
parent.InsertAsRc(child);
}
hot = parent;
succ = w.RightChild;
}
return succ;
}
/// <summary>
/// 替换节点:将 B 节点替换为 A
/// </summary>
private BinNode<T> Replace(BinNode<T> A, BinNode<T> B)
{
if (A.IsRoot())
{
Root = B;
if (null != Root)
{
Root.ParentNode = null;
}
return Root;
}
if (null != B)
{
B.ParentNode = A.ParentNode;
}
if (A.IsRChild())
{
A.ParentNode.RightChild = B;
}
else
{
A.ParentNode.LeftChild = B;
}
return B;
}
/// <summary>
/// 节点的直接后继
/// 如果有右孩子,则取右孩子及子孙后代中最小者
/// 否则,取左孩子及子孙后代中最大者
/// </summary>
private BinNode<T> NodeSucc(BinNode<T> node)
{
if (node.HasLChild())
{
node = node.LeftChild;
while (null != node && node.HasRChild())
{
node = node.RightChild;
}
}
else
{
node = node.RightChild;
while (node.HasLChild())
{
node = node.LeftChild;
}
}
return node;
}
/// <summary>
/// 查询:返回查询结果,如果存在则 _hot 为查询结果的父节点
/// </summary>
/// <param name="t"></param>
/// <returns></returns>
public virtual BinNode<T> Search(T t)
{
if (null == Root || t.CompareTo(Root.Element) == 0)
{
_hot = null;
return Root;
}
_hot = Root;
while (null != _hot)
{
BinNode<T> c = _hot.Element.CompareTo(t) > 0 ? _hot.LeftChild : _hot.RightChild;
if (null == c || c.Element.CompareTo(t) == 0)
{
return c;
}
_hot = c;
}
return _hot;
}
/// <summary>
/// BST 节点旋转变换统一算法(3节点 + 4子树),返回调整之后局部子树根节点的位置
/// 注意:尽管子树根会正确指向上层节点(如果存在),但反向的联接须由上层函数完成
/// </summary>
protected BinNode<T> RotateAt(BinNode<T> v)
{
if (null == v)
{
return v;
}
BinNode<T> p = v.ParentNode;
BinNode<T> g = p.ParentNode; // 视v、p和g相对位置分四种情况
if (p.IsLChild())
{
if (v.IsLChild())
{
p.ParentNode = g.ParentNode; // 向上联接
return Connect34(v, p, g, v.LeftChild, v.RightChild, p.RightChild, g.RightChild);
}
else
{
v.ParentNode = g.ParentNode; // 向上联接
return Connect34(p, v, g, p.LeftChild, v.LeftChild, v.RightChild, g.RightChild);
}
}
else
{
if (v.IsRChild())
{
p.ParentNode = g.ParentNode;// 向上联接
return Connect34(g, p, v, g.LeftChild, p.LeftChild, v.LeftChild, v.RightChild);
}
else
{
v.ParentNode = g.ParentNode;// 向上联接
return Connect34(g, v, p, g.LeftChild, v.LeftChild, v.RightChild, p.RightChild);
}
}
}
/// <summary>
/// 按照 "3 + 4" 结构联接3个节点及其4棵子树,返回重组之后的局子树根节点位置(即b)
/// 子树根节点与上层节点之间的双向联接,均须由上层调用者完成
/// 可用于AVL 和 RedBlack 的局部平衡调整
/// </summary>
/// <returns></returns>
protected BinNode<T> Connect34(BinNode<T> a, BinNode<T> b, BinNode<T> c, BinNode<T> T0, BinNode<T> T1, BinNode<T> T2, BinNode<T> T3)
{
a.LeftChild = T0;
if (null != T0)
{
T0.ParentNode = a;
}
a.RightChild = T1;
if (null != T1)
{
T1.ParentNode = a;
}
c.LeftChild = T2;
if (null != T2)
{
T2.ParentNode = c;
}
c.RightChild = T3;
if (null != T3)
{
T3.ParentNode = c;
}
b.LeftChild = a;
a.ParentNode = b;
b.RightChild = c;
c.ParentNode = b;
UpdateHeight(a);
UpdateHeight(c);
UpdateHeight(b);
return b;
}
}
下面函数是平衡二叉树的代码,二叉搜索树部分直接略过即可
BinNode<T> RotateAt(BinNode<T> v)
BinNode<T> Connect34(BinNode<T> a, BinNode<T> b, BinNode<T> c, BinNode<T> T0, BinNode<T> T1, BinNode<T> T2, BinNode<T> T3)