C++ 二叉搜索树

本文介绍了二叉搜索树(BST),它是特殊二叉树,适合有序集合存储。阐述了其定义、性质、局限性和应用场景,详细讲解了BST的插入、查找、删除等操作的实现,包括递归实现方式,还进行了性能分析,最后介绍了BST在K模型、KV模型等方面的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

一、二叉搜索树概念

1、二叉搜索树的定义

2、二叉搜索树的性质

3、二叉搜索树的局限性

4、应用场景

二、二叉搜索树的实现

1、二叉搜索树的插入

2、中序遍历

3、二叉搜索树的查找

4、二叉搜索树的删除

5、构造

6、拷贝构造

7、析构

8、赋值

三、递归实现二叉搜索树

1、插入

2、查找

3、删除

四、性能分析

五、应用

1、K模型

2、KV模型

3、key-value

测试

六、完整版

七、测试函数


一、二叉搜索树概念

当然可以。二叉搜索树(Binary Search Tree, BST)是一种特殊的二叉树数据结构,它在计算机科学中有广泛的应用,尤其是在需要快速查找、插入和删除操作的场景中。

1、二叉搜索树的定义

二叉搜索树是一种具有以下性质的二叉树:

  1. 左子树的性质:如果一个节点有左子树,则左子树上所有节点的值均小于它的根节点的值。
  2. 右子树的性质:如果一个节点有右子树,则右子树上所有节点的值均大于它的根节点的值。
  3. 递归性质:它的左、右子树也分别为二叉搜索树。

这些性质使得二叉搜索树非常适合用于有序集合的存储,并且使得查找、插入和删除等操作的时间复杂度在平均情况下为 O(log_2N),其中 nn 是树中节点的数量。

2、二叉搜索树的性质

  • 中序遍历有序:二叉搜索树的中序遍历(左-根-右)会产生一个升序排列的节点值序列。
  • 查找最小/最大元素:在二叉搜索树中查找最小元素,只需一直沿着左子树走到底;查找最大元素,只需一直沿着右子树走到底。

由于这个性质,中序遍历二叉搜索树时,会先遍历左子树,然后输出根节点的值,最后遍历右子树。这样就可以按照从小到大的顺序输出二叉搜索树中的节点值。

中序遍历:1 3 4 6 7 8 10 13 14 

3、二叉搜索树的局限性

虽然二叉搜索树提供了高效的查找、插入和删除操作,但它也有一些局限性:

  • 不平衡问题:在最坏的情况下,如果连续插入的键值都是递增或递减的,二叉搜索树可能会退化成一个链表,导致时间复杂度退化为 O(n)O(n)。
  • 自平衡二叉搜索树:为了避免不平衡问题,人们发明了一些自平衡的二叉搜索树,如AVL树和红黑树等,它们通过在插入和删除操作期间自动调整树的结构来保持树的高度尽可能平衡,从而保证操作的平均时间复杂度为 O(log⁡n)O(logn)。

4、应用场景

二叉搜索树因其高效的查找、插入和删除性能,在多种场合中得到了广泛应用,例如:

  • 文件系统:用于索引文件和目录。
  • 数据库管理系统:用于索引表中的记录。
  • 符号表:用于存储键值对,例如编译器中的符号表。
  • 优先队列:通过适当的键值映射,可以构建优先队列。

二、二叉搜索树的实现

namespace hbr
{
    template<class K>
    struct BSTreeNode
    {
    	BSTreeNode<K>* _left;
	    BSTreeNode<K>* _right;
    	K _key;
    	BSTreeNode(const K& key)
    		:_left(nullptr)
    		, _right(nullptr)
    		, _key(key)
    	{}
    };
  
    template<class K>
    class BSTree
    {
    typedef BSTreeNode<K> Node;
    public:
        bool Insert(const K& key){}
        bool Find(const K& key){}
        bool Erase(const K& key){}
        void _InOrder(Node* root){}

    private:
		Node* _root = nullptr;
	};
}

首先,定义了一个 BSTreeNode 结构体,表示二叉搜索树的节点。每个节点包含一个键值 _key,以及指向左子节点 _left 和右子节点 _right 的指针。

接下来,定义了一个 BSTree 类,表示二叉搜索树。该类包含一个私有成员 _root,指向树的根节点。

BSTree 类提供以下公有成员函数:

  1. Insert 函数用于向二叉搜索树中插入一个节点。它接受一个键值 key 作为参数,并根据二叉搜索树的性质找到合适的位置插入节点。

  2. Find 函数用于在二叉搜索树中查找一个节点。它接受一个键值 key 作为参数,并根据二叉搜索树的性质进行查找,返回是否找到该节点。

  3. Erase 函数用于从二叉搜索树中删除一个节点。它接受一个键值 key 作为参数,并根据二叉搜索树的性质找到要删除的节点,并进行删除操作。

  4. _InOrder 函数用于中序遍历,由于二叉搜索树的性质使用中序遍历可以按照升序输出每个节点。

namespace test
{
    template<class K>
    class BSTree
    {
        typedef BSTreeNode<K> Node;

    public:
        // 默认构造函数
        BSTree() = default; 

        // 复制构造函数
        BSTree(const BSTree<K>& t)
        {
            _root = Copy(t._root); // 深拷贝整个树
        }

        // 赋值运算符重载
        BSTree<K>& operator=(BSTree<K> t)
        {
            swap(_root, t._root); // 使用交换技巧完成赋值操作
            return *this;
        }

        // 析构函数
        ~BSTree()
        {
            Destroy(_root); // 销毁整个树
        }

        // 插入一个新节点
        bool Insert(const K& key)
        {
            if (_root == nullptr)
            {
                _root = new Node(key); // 如果树为空,则创建根节点
                return true;
            }
            Node* parent = nullptr;
            Node* cur = _root;
            while (cur) 
            {
                if (cur->_key < key) 
                {
                    parent = cur;
                    cur = cur->_right; // 向右子树移动
                }
                else if (cur->_key > key) 
                {
                    parent = cur;
                    cur = cur->_left; // 向左子树移动
                }
                else 
                {
                    return false; // 如果键已存在,则不插入
                }
            }
            cur = new Node(key); // 创建新节点
            if (parent->_key < key) 
            {
                parent->_right = cur; // 插入到父节点的右子树
            }
            else 
            {
                parent->_left = cur; // 插入到父节点的左子树
            }
            return true;
        }

        // 查找一个节点
        bool Find(const K& key)
        {
            Node* cur = _root;
            while (cur) 
            {
                if (cur->_key < key) 
                {
                    cur = cur->_right; // 向右子树移动
                }
                else if (cur->_key > key) 
                {
                    cur = cur->_left; // 向左子树移动
                }
                else 
                {
                    return true; // 找到了键值
                }
            }
            return false; // 没有找到键值
        }

        // 删除一个节点
        bool Erase(const K& key)
        {
            Node* parent = nullptr;
            Node* cur = _root;
            while (cur) 
            {
                if (cur->_key < key) 
                {
                    parent = cur;
                    cur = cur->_right; // 向右子树移动
                }
                else if (cur->_key > key) 
                {
                    parent = cur;
                    cur = cur->_left; // 向左子树移动
                }
                else // 找到了要删除的节点
                {
                    if (cur->_left == nullptr) // 右子树为空的情况
                    {
                        if (cur == _root) 
                        {
                            _root = cur->_right; // 根节点的右子树成为新的根节点
                        }
                        else 
                        {
                            if (parent->_left == cur) 
                            {
                                parent->_left = cur->_right; // 替换父节点的左子树
                            }
                            else 
                            {
                                parent->_right = cur->_right; // 替换父节点的右子树
                            }
                        }
                        delete cur; // 删除旧节点
                    }
                    else if (cur->_right == nullptr) // 左子树为空的情况
                    {
                        if (cur == _root) 
                        {
                            _root = cur->_left; // 根节点的左子树成为新的根节点
                        }
                        else 
                        {
                            if (parent->_left == cur) 
                            {
                                parent->_left = cur->_left; // 替换父节点的左子树
                            }
                            else 
                            {
                                parent->_right = cur->_left; // 替换父节点的右子树
                            }
                        }
                        delete cur; // 删除旧节点
                    }
                    else // 既有左子树又有右子树的情况
                    {
                        Node* pminRight = cur;
                        Node* minRight = cur->_right;
                        while (minRight->_left) 
                        {
                            pminRight = minRight;
                            minRight = minRight->_left; // 找到右子树中的最小节点
                        }
                        cur->_key = minRight->_key; // 使用右子树的最小值替换当前节点的键值
                        if (pminRight->_left == minRight) 
                        {
                            pminRight->_left = minRight->_right; // 替换右子树中最小节点的位置
                        }
                        else 
                        {
                            pminRight->_right = minRight->_right; // 替换右子树中最小节点的位置
                        }
                        delete minRight; // 删除旧的最小节点
                    }
                    return true; // 成功删除
                }
            }
            return false; // 没有找到要删除的键值
        }

        // 查找一个节点
        bool Find(const K& key)
        {
            return _FindR(_root, key); // 使用递归函数查找
        }

        // 插入一个新节点(递归版本)
        bool InsertR(const K& key)
        {
            return _InsertR(_root, key); // 使用递归函数插入
     
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Han同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值