C++进阶之路---二叉搜索树详解 | 具体实现

顾得泉:个人主页

个人专栏:《Linux操作系统》 《C++从入门到精通》  《LeedCode刷题》

键盘敲烂,年薪百万!


一、二叉搜索树简介

       二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:

       1.若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
       2.若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
       3.它的左右子树也分别为二叉搜索树


二、详细操作

       int a[ ] = {8, 3, 1, 10, 6, 4, 7, 14, 13}; 

1.查找

       a、从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找。

       b、最多查找高度次,走到到空,还没找到,这个值不存在。

2.插入

插入的具体过程如下

       a. 树为空,则直接新增节点,赋值给root指针

       b. 树不空,按二叉搜索树性质查找插入位置,插入新节点

3. 删除

       首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情况:

       a. 要删除的结点无孩子结点
       b. 要删除的结点只有左孩子结点
       c. 要删除的结点只有右孩子结点
       d. 要删除的结点有左、右孩子结点

       看起来有待删除节点有4中情况,实际情况a可以与情况b或者c合并起来,因此真正的删除过程
如下:

       情况b:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点–直接删除
       情况c:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点–直接删除
       情况d:在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点中,再来处理该结点的删除问题–替换法删除。


 三、具体实现

#include<iostream>
using namespace std;

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)
    {
        if (_root == nullptr)
        {
            _root = new Node(key);
            return true;
        }
        Node* parent = nullptr;
        Node* cur = _root;
        while (cur)
        {
            parent = cur;
            if (cur->_key < key)
            {
                cur = cur->_right;
            }
            else if (cur->_key > key)
            {
                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 (cur == parent->_left)
                        {
                            parent->_left = cur->_right;
                        }
                        else 
                        {
                            parent->_right = cur->_right;
                        }
                    }
                    delete cur;
                }
                else if (cur->_right == nullptr)
                {
                    if (cur == _root)
                    {
                        _root = cur -> _left;
                    }
                    else
                    {
                        if (cur == parent->_left)
                        {
                            parent->_left = cur->_left;
                        }
                        else
                        {
                            parent->_right = cur->_left;
                        }
                    }
                    delete cur;
                }
                else
                {
                    Node* parent = cur;
                    Node* subleft = cur->_right;
                    while (subleft->_left)
                    {
                        parent = subleft;
                        subleft = subleft->_left;
                
                    }
                    swap(cur->_key, subleft->_key);

                    if (subleft == parent->_left)
                    {
                        parent->_left = subleft->_right;
                    }
                    else
                    {
                        parent->_right = subleft->_right;
                    }
                    delete subleft;
                }
                return true;
            }
        }
        return false;
    }
    ~BSTree()
    {
        Destory(_root);
    }

    bool EraseR(const K& key)
    {
        return _EraseR(_root, key);
    }
    bool InsertR(const K& key)
    {
        return _InsertR(_root,key);
    }
    bool findR(const K& key)
    {
        return _findR(_root, key);
    }
    void InOrder()
    {
        _InOrder(_root);
        cout << endl;
    }
    BSTree(const BSTree<K>& t)
    {
        _root = Copy(t._root);
    }
    //C++11
    BSTree()
    {}
    
    BSTree<K>& operator=(BSTree<K> t)
    {
        swap(_root,t._root);
        return *this;
    }
private:
    Node* _root = nullptr;
    Node* Copy(Node* root)
    {
        if (root == nullptr)
            return nullptr;

        Node* newRoot = new Node(root->_key);
        newRoot->_left = Copy(root->_left);
        newRoot->_right = Copy(root->_right);

        return newRoot;

    }
    void Destory(Node*& root)
    {

        if (root == nullptr)
            return;
        Destory(root->_left);
        Destory(root->_right);
        delete root;
        root = nullptr;
    }
    void _InOrder(Node* root)
    {
        if (root == nullptr)
            return;
        _InOrder(root->_left);
        cout << root->_key << " ";
        _InOrder(root->_right);
    }
    bool _InsertR(Node*& root,const K& key)
    {
        if (root == nullptr)
        {
            root = new Node(key);
            return true;
        }
        if (root->_key < key)
        {
            return  _InsertR(root->_right,key);
        }
        else if (root->_key > key)
        {
            return  _InsertR(root->_left,key);
        }
        else
        {
            return false;
        }
    }
    bool _findR(Node* root, const K* key)
    {
        if (root == nullptr)
            return false;
        if (root->_key < key)
        {
            return _findR(root->_right, key);
        }
        else if (root->_key > key)
        {
            return _findR(root->_left, key);
        }
        else
        {
            return true;
        }
    }
    bool _EraseR(Node*& root, const K& key)
    {
        if (root == nullptr)
            return false;
        if (root->_key < key)
        {
            return _EraseR(root->_right, key);
        }
        else if (root->_key > key)
        {
            return _EraseR(root->_left, key);
        }
        else
        {
            if (root->_left == nullptr)
            {
                Node* del = root;
                root = root->_right;
                delete del;
            }
            else if (root->_right == nullptr)
            {
                Node* del = root;
                root = root->_left;
                delete del;
            }
            else
            {
                Node* subleft = root->_right;
                while (subleft->_left)
                {
                    subleft = subleft->_left;
                }
                swap(subleft->_key, root->_key);

                return _EraseR(root->_right, key);
            }
        }
        return false;
    }  
};
int main()
{
    int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };
    BSTree<int> bt;
    for (auto e : a)
    {
        bt.InsertR(e);
    }
    bt.InOrder();

    bt.EraseR(14);
    bt.InOrder();

    bt.EraseR(3);
    bt.InOrder();

    bt.EraseR(8);
    bt.InOrder();

    for (auto e : a)
    {
        bt.EraseR(e);
        bt.InOrder();
    }
    return 0;
}

四、具体应用

1.K模型

       K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到的值。

比如:给一个单词word,判断该单词是否拼写正确,具体方式如下:

       1.以词库中所有单词集合中的每个单词作为key,构建一棵二叉搜索树

       2.在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。

2.KV模型

       每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对。该种方式在现实生活中非常常见:

       比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英文单词与其对应的中文<word, chinese>就构成一种键值对;

       再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出现次数就是<word, count>就构成一种键值对。

//改造二叉搜索树为KV结构
template<class K,class V>
struct BSTreeNode
{
    BSTreeNode<K,V>* _left;
    BSTreeNode<K,V>* _right;
    K _key;
    V _value;

    BSTreeNode(const K& key,const V& value)
        :_left(nullptr)
        , _right(nullptr)
        , _key(key)
        ,_value(value)
    {}
};
template<class K,class V>
class BSTree
{
    typedef BSTreeNode<K,V> Node;
public:
    bool Insert(const K& key,const V& value)
    {
        if (_root == nullptr)
        {
            _root = new Node(key,value);
            return true;
        }

        Node* parent = nullptr;
        Node* cur = _root;
        while (cur)
        {
            parent = cur;
            if (cur->_key < key)
            {
                cur = cur->_right;
            }
            else if (cur->_key > key)
            {
                cur = cur->_left;
            }
            else
            {
                return false;
            }
        }
        cur = new Node(key,value);
        if (parent->_key < key)
        {
            parent->_right = cur;
        }
        else
        {
            parent->_left = cur;

        }
        return true;
    }

    Node* 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 cur;
            }
        }
        return nullptr;

    }
    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 (cur == parent->_left)
                        {
                            parent->_left = cur->_right;
                        }
                        else
                        {
                            parent->_right = cur->_right;
                        }
                    }
                    delete cur;
                }
                else if (cur->_right == nullptr)
                {
                    if (cur == _root)
                    {
                        _root = cur->_left;
                    }
                    else
                    {
                        if (cur == parent->_left)
                        {
                            parent->_left = cur->_left;
                        }
                        else
                        {
                            parent->_right = cur->_left;
                        }
                    }
                    delete cur;
                }
                else
                {
                    Node* parent = cur;
                    Node* subleft = cur->_right;
                    while (subleft->_left)
                    {
                        parent = subleft;
                        subleft = subleft->_left;

                    }
                    swap(cur->_key, subleft->_key);

                    if (subleft == parent->_left)
                    {
                        parent->_left = subleft->_right;
                    }
                    else
                    {
                        parent->_right = subleft->_right;
                    }
                    delete subleft;
                }
                return true;
            }
        }
        return false;
    }
    ~BSTree()
    {
        Destory(_root);
    }
    void InOrder()
    {
        _InOrder(_root);        
    }
private:
    Node* _root = nullptr;    
    void Destory(Node*& root)
    {
        if (root == nullptr)
            return;
        Destory(root->_left);
        Destory(root->_right);
        delete root;
        root = nullptr;
    }
    void _InOrder(Node* root)
    {
        if (root == nullptr)
            return;
        _InOrder(root->_left);
        cout << root->_key << ":" << root->_value << endl;
        _InOrder(root->_right);
    }
};

五、性能分析

       插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。

       对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多

但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:

       最优情况下,二叉搜索树为完全二叉树(或者接近完全二叉树),其平均比较次数为:O(logN)

       最差情况下,二叉搜索树退化为单支树(或者类似单支),其平均比较次数为:O(N)

思考:如果退化成单支树,二叉搜索树的性能就失去了。那能否进行改进,不论按照什么次序插入关键码,二叉搜索树的性能都能达到最优?那么请期待后续章节学习的AVL树和红黑树。


结语:C++关于二叉搜索树的分享到这里就结束了,希望本篇文章的分享会对大家的学习带来些许帮助,如果大家有什么问题,欢迎大家在评论区留言~~~ 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/451042.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

设计模式九:装饰器模式

文章目录 1、装饰器模式2、示例3、装饰器模式与适配器模式4、装饰器模式和代理模式5、java io流的装饰器模式 1、装饰器模式 装饰器模式&#xff08;Decorator Pattern&#xff09;允许向一个现有的对象添加新的功能&#xff0c;同时又不改变其结构。这种类型的设计模式属于结构…

H5 微商宣传引流跳转微信单页源码

源码名称&#xff1a;H5 微商宣传引流跳转微信单页源码 源码介绍&#xff1a;一款微商宣传引流单页源码&#xff0c;源码带有导师微信二维码&#xff0c;点击复制微信号并跳转到微信功能【跳转后需自行贴贴搜索】。可用于各种微商团队宣传。 需求环境&#xff1a;H5 下载地址…

如何将应用一键部署至多个环境?丨Walrus教程

在 Walrus 平台上&#xff0c;运维团队在资源定义&#xff08;Resource Definition&#xff09;中声明提供的资源类型&#xff0c;通过设置匹配规则&#xff0c;将不同的资源部署模板应用到不同类型的环境、项目等。与此同时&#xff0c;研发人员无需关注底层具体实现方式&…

基与HTML5的塔防游戏设计与实现

目 录 摘 要 I Abstract II 引 言 1 1 项目背景与相关技术 3 1.1 背景与发展简介 3 1.2 HTML5技术及其优势 4 1.3 JavaScript开发的优势与劣势 4 1.4 CSS样式表在开发中的用处 5 1.5 本章小结 6 2 系统分析 7 2.1 需求分析 7 2.2 问题分析 7 2.3 流程设计 7 2.3 功能分析 8 2.…

私立医院的革命者:大数据解决方案全面解析

第一部分&#xff1a;背景 在信息化飞速发展的今天&#xff0c;医疗行业正经历着一场深刻的数字化转型。特别是对于私立医院来说&#xff0c;要在这个变革的浪潮中立于不败之地&#xff0c;就必须拥抱新技术&#xff0c;优化服务流程&#xff0c;提高医疗质量。大数据技术&…

基于深度学习的植物类别检测系统(含UI界面、yolov8、Python代码、数据集)

项目介绍 项目中所用到的算法模型和数据集等信息如下&#xff1a; 算法模型&#xff1a;     yolov8 yolov8主要包含以下几种创新&#xff1a;         1. 可以任意更换主干结构&#xff0c;支持几百种网络主干。 数据集&#xff1a;     网上下载的数据集&#x…

【Linux】常见的基本指令(下)

在本篇博客中&#xff0c;继续介绍Linux的常见的基本指令。 一.find指令 find指令是一条搜索指令&#xff0c;在目录结构中搜索文件。 find [目录名] -name [文件名] 在指定的目录下以文件名的搜索方式去搜索文件 二.which指令 which指令是只用来搜索命令在那个路径下…

Ableton Live 12 Suite:音乐创作的全能工作站 mac版

在数字音乐制作的领域中&#xff0c;Ableton Live 11 Suite 无疑是引领潮流的旗舰产品。作为一款综合性的音乐制作和演出软件&#xff0c;它提供了从创作灵感的萌芽到最终作品完成的全方位解决方案。 Ableton Live 12 Suite Mac版软件获取 Ableton Live 11 Suite 凭借其强大的…

登录与注册功能(简单版)(1)登录

目录 1、需求 2、怎样实现 3、步骤 1&#xff09;创建login.html 2&#xff09;创建user数据表 3&#xff09;IDEA连接数据库 4&#xff09;pom.xml中添加MyBatis和MySQL驱动坐标 5&#xff09;创建User实体类 6&#xff09;创建UserMapper.xml映射文件 7&#xff0…

实体好做,还是电商好做?最适合新手的是哪种?

我是电商珠珠 时间过得很快&#xff0c;距离2025年转眼间也就只剩下了9个月&#xff0c;部分人想要充分利用自己的时间去做一些可以赚钱的项目。 现在创业要么做实体店&#xff0c;要么做自媒体&#xff0c;要么就做电商。按照现在的经济发展趋势&#xff0c;开实体店甚至不能…

Tiktok视频播放为何为0?4大原因小白需了解

原因一、养号失败&#xff0c;被判为营销号 注册了tiktok账号以后&#xff0c;是需要一段时间进行养号&#xff0c;培养账号权重的&#xff01;而有些小伙伴未经养号&#xff0c;直接注册上手开始发视频&#xff01;这样很容易导致视频没有播放&#xff01;因为这样连续的一系…

比派电器T6白色系高速吹风机,高品质保证下,追求极致性价比

广东比派电器科技有限公司于2020年成立于东莞市松山湖高新技术企业园区融易大厦&#xff0c;公司聚焦于小家电的研发&#xff0c;生产&#xff0c;销售。专注在小家电的PCBA研发&#xff0c;产品设计&#xff0c;成品生产。提供小家电产品一站式解决方案&#xff0c;致力于成为…

铁威马TOS 6即将登场,全新设计更多功能抢先看!

错过了TOS 6内测的铁粉们注意啦&#xff01; 很高兴与大家宣布 铁威马TOS 6 Beta 将在本月与大家见面 时隔一年多 TOS 6历经严格测试与精细优化 焕然一新的用户界面 由内而外展现全新风采 今天小马就来给大家小剧透 TOS 6新功能抢先看 TOS 6是铁威马迄今为止“最友好最美…

深度强化学习(三)(DQN)

深度强化学习&#xff08;三&#xff09;DQN与Q学习 一.DQN 通过神经网络来近似最优动作价值函数 Q ∗ ( a t , s t ) Q_*(a_t,s_t) Q∗​(at​,st​),在实践中, 近似学习“先知” Q ⋆ Q_{\star} Q⋆​ 最有效的办法是深度 Q \mathrm{Q} Q网络 (deep Q network, 缩写 DQN)…

Java进程CPU高负载排查

Java进程CPU高负载排查步骤_java进程cpu使用率高排查_YouluBank的博客-CSDN博客 【问题定位】使用arthas定位CPU高的问题_arthas cpu高_秋装什么的博客-CSDN博客 CPU飙升可能原因 CPU 上下文切换过多。 对于 CPU 来说&#xff0c;同一时刻下每个 CPU 核心只能运行-个线程&…

深度学习模型部署(六)TensorRT工作流and入门demo

TensorRT工作流程 官方给出的步骤&#xff1a; 总结下来可以分为两大部分&#xff1a; 模型生成&#xff1a;将onnx经过一系列优化&#xff0c;生成tensorrt的engine模型 选择batchsize&#xff0c;选择精度precision&#xff0c;模型转换 模型推理&#xff1a;使用python或…

SpringBoot(容器功能)

文章目录 1.Configuration 添加/注入bean1.注入bean1.编写一个JavaBean&#xff0c;Monster.java2.创建一个config文件夹&#xff08;名字任意&#xff09;&#xff0c;用于存放配置Bean的类&#xff08;相当于配置文件&#xff09;3.BeanConfig.java4.测试使用 MainApp.java2.…

Spring中使用自带@Autowired注解实现策略模式

场景 SpringBoot中策略模式工厂模式业务实例(接口传参-枚举类查询策略映射关系-执行不同策略)规避大量if-else&#xff1a; SpringBoot中策略模式工厂模式业务实例(接口传参-枚举类查询策略映射关系-执行不同策略)规避大量if-else_springboot编写策略工厂-CSDN博客 设计模式…

NASA和IBM联合开发的 2022 年多时相土地分类数据集

简介 美国国家航空航天局&#xff08;NASA&#xff09;和国际商业机器公司&#xff08;IBM&#xff09;合作&#xff0c;利用大规模卫星和遥感数据&#xff0c;包括大地遥感卫星和哨兵-2 号&#xff08;HLS&#xff09;数据&#xff0c;创建了地球观测人工智能基础模型。通过奉…

【Windows】解决Windows磁盘有锁和感叹号方法

文章目录 1、概述2、效果3、解决方案3.1、先看自己电脑环境3.2、查看BitLocker情况3.3、查看设备加密 4、其他方案5、BitLocker5.1、BitLocker 是什么5.2、BitLocker 作用5.3、BitLocker 和 TPM 1、概述 目前在整理自己新电脑的软件&#xff0c;无意间电脑磁盘有锁和感叹号的标…