二叉搜索树

二叉搜索树

了解二叉搜索树前提要了解 二叉树 二叉树概念
二叉搜索树(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)
  • 22
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值