C++进阶 二叉搜索树的讲解

news2024/11/15 20:00:45

在这里插入图片描述

二叉搜索树的概念

二叉搜索树又称为二叉排序树。

  • 二叉搜索树的性质
    • 若它的左子树不为空,则左子树上所有结点的值都小于等于根结点的值
    • 若它的右子树不为空,则右子树上所有结点的值都大于等于根结点的值
    • 它的左右子树也分别为二叉搜索树
    • 二叉搜索树中可以支持插入相等的值,也可以不支持插入相等的值。

二叉搜索树的插入

    1. 树为空,则直接新增结点,赋值给 root 指针
    1. 树不空,按二叉搜索树性质,插入值比当前结点大往右走,插入值比当前结点小往左走,找到空位置,插入新结点。
    1. 如果支持插入相等的值,插入值跟当前结点相等的值可以往右走,也可以往左走,找到空位置,插入新结点。(要注意的是要保持逻辑一致性,插入相等的值不要一会往右走,一会往左走)

例子:

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

请添加图片描述

代码:

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)
    {
        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, value);
    if (parent->_key < key)
    {
        parent->_right = cur;
    }
    else
    {
        parent->_left = cur;
    }

    return true;
}

二叉搜索树的查找

    1. 从根开始比较,查找 x,x 比根的值大则往边走查找,x 比根值小则往边走查找。
    1. 最多查找高度次,走到到空,还没找到,这个值不存在。
    1. 如果不支持插入相等的值,找到 x 即可返回。
    1. 如果支持插入相等的值,意味着有多个 x 存在,一般要求查找中序的第一个 x

代码:

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;
}

二叉搜索树的删除

首先查找元素是否在二叉搜索树中,如果不存在,则返回 false。
如果查找元素存在则分以下四种情况分别处理:(假设要删除的结点为 N)

  1. 要删除结点 N 左右孩子均为空
    • 解决方法:把 N 结点的父亲对应孩子指针指向空,直接删除 N 结点
  2. 要删除的结点 N 左孩子位空,右孩子结点不为空
    • 解决方法:把 N 结点的父亲对应孩子指针指向 N 的右孩子,直接删除 N 结点
  3. 要删除的结点 N 右孩子位空,左孩子结点不为空
    • 解决方法:把 N 结点的父亲对应孩子指针指向 N 的左孩子,直接删除 N 结点
  4. 要删除的结点 N 左右孩子结点均不为空
    • 解决方法:无法直接删除 N 结点,因为 N 的两个孩子无处安放,只能用替换法删除。**找 N 左子树的值最大结点 R(最右结点)**或者 N 右子树的值最小结点 R(最左结点)替代 N,因为这两个结点中任意一个,放到 N 的位置,都满足二叉搜索树的规则。替代 N 的意思就是 N 和 R 的两个结点的值交换,转而变成删除 R 结点,R 结点符合情况 2 或情况 3,可以直接删除。

代码:

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
            {
                // 左右节点都不为空
                // 这里默认替换右子树最左节点c
                Node *replaceParent = cur;
                Node *replace = cur->_right;//进入根节点的右子树
                while (replace->_left)//迭代左节点,找到右子树的最左节点
                {
                    replaceParent = replace;//记录父节点
                    replace = replace->_left;//迭代左节点
                }
                //此时替换节点(replace)的左节点一定为空
                cur->_key = replace->_key;//交换值

                if (replaceParent->_left == replace)//如果替换节点是左节点
                    replaceParent->_left = replace->_right;//将父节点的左赋值为替换节点的右,因为替换节点的左节点一定是空的,因为是按照根节点的右子树的最左节点查找的。
                else//如果替换节点是右节点
                    replaceParent->_right = replace->_right;//将父节点的右赋值为替换节点的右,因为替换节点的左节点一定是空的,因为是按照根节点的右子树的最左节点查找的。

                delete replace;//删除替换节点
            }

            return true;
        }
    }

    return false;
}

二叉搜索树的析构

  • 递归析构
public:
~BSTree()
{
    Destroy(_root);
    _root = nullptr;
}

private:
void Destroy(Node *root)
{
    if (root == nullptr)
        return;

    Destroy(root->_left);
    Destroy(root->_right);
    delete root;
}


二叉搜索树的深拷贝

BSTree(const BSTree &t)
{
    _root = Copy(t._root);
}

Node *Copy(Node *root)
{
    if (root == nullptr)
        return nullptr;

    Node *newRoot = new Node(root->_key, root->_value);
    newRoot->_left = Copy(root->_left);
    newRoot->_right = Copy(root->_right);
    return newRoot;
}

二叉搜索树的=重载

BSTree &operator=(BSTree tmp)
{
    swap(_root, tmp._root);//tmp为拷贝,可以直接交换,不影响tmp
    return *this;
}

完整代码

#pragma once
#include <iostream>
using namespace std;

template <class K>
struct BSTNode
{
    K _key;
    BSTNode<K> *_left;
    BSTNode<K> *_right;

    BSTNode(const K &key)
        : _key(key), _left(nullptr), _right(nullptr)
    {
    }
};

namespace key
{

    template <class K>
    class BSTree
    {
        typedef BSTNode<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)
            {
                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;
        }

        void Inorder() // 中序遍历
        {
            _Inorder(_root);
        }

        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 *replaceParent = cur;
                        Node *replace = cur->_right;
                        while (replace->_left)
                        {
                            replaceParent = replace;
                            replace = replace->_left;
                        }

                        cur->_key = replace->_key;

                        if (replaceParent->_left == replace)
                            replaceParent->_left = replace->_right;
                        else
                            replaceParent->_right = replace->_right;

                        delete replace;
                    }

                    return true;
                }
            }

            return false;
        }

    private:
        void _Inorder(Node *root)
        {
            if (root == nullptr)
            {
                return;
            }
            _Inorder(root->_left);
            cout << root->_key << ' ';
            _Inorder(root->_right);
        }

    private:
        Node *_root = nullptr;
    };

}


在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Python】基本使用

目录 变量的类型 整数 int 浮点数 float 字符串 str 字符串长度 格式化字符串 布尔类型 动态类型 注释 获取输入 浮点数比较 多元赋值 for循环 函数的定义和调用 创建函数/定义函数 调用函数/使用函数 列表 创建列表 切片操作 遍历列表 新增元素 判断元…

2024上半年国产操作系统卖疯了!麒麟4.9亿,统信1.9亿!

昨天一篇关于国产数据库上市公司的财报分析&#xff0c;没想到还小&#x1f525;了一把。实际上国产数据库与操作系统是密不可分的&#xff0c;既然用户要进行全面国产化&#xff0c;那么除了数据库&#xff0c;底层服务器操作系统&#xff0c; 一定也会是国产操作系统。 从20…

JavaScript事件处理和常用对象

文章目录 前言一、事件处理程序 1.JavaScript 常用事件2.事件处理程序的调用二、常用对象 1.Window 对象2.String 对象3.Date 对象总结 前言 JavaScript 语言是事件驱动型的。这意味着&#xff0c;该门语言可以通过事件触发来调用某一函数或者一段代码。该文还简单介绍了Window…

讨论人机交互研究中大语言模型的整合与伦理问题

概述 论文地址&#xff1a;https://arxiv.org/pdf/2403.19876.pdf 近年来&#xff0c;大规模语言模型发展迅速。它们给研究和教育领域带来了许多变化。这些模型也是对人机交互&#xff08;HCI&#xff09;研究过程的有力补充&#xff0c;可以分析定性和定量数据&#xff0c;再…

4.C_数据结构_队列

概述 什么是队列&#xff1a; 队列是限定在两端进行插入操作和删除操作的线性表。具有先入先出(FIFO)的特点 相关名词&#xff1a; 队尾&#xff1a;写入数据的一段队头&#xff1a;读取数据的一段空队&#xff1a;队列中没有数据&#xff0c;队头指针 队尾指针满队&#…

springboot 整合quartz定时任务

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、pom的配置1.加注解 二、使用方法1.工程图2.创建工具类 三、controller 实现 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 提示&a…

微服务、云计算、分布式开发全套课程课件,来原于企培和多年大厂工作提炼

本课内容为笔者16年企业工作期间企培经验总结的 全套课件。需要自取&#xff0c;已分块和整体上传至资源下载中。 全部来源于笔者多年企业培训迭代整理&#xff0c;并做了特殊处理&#xff0c;所以内容无涉密和版权麻烦。 课件内容全部来源于笔者在京东、58、阿里&#xff1b;中…

第二百三十二节 JPA教程 - JPA教程 - JPA ID自动生成器示例、JPA ID生成策略示例

JPA教程 - JPA ID自动生成器示例 我们可以将id字段标记为自动生成的主键列。 数据库将在插入时自动为id字段生成一个值数据到表。 例子 下面的代码来自Person.java。 package cn.w3cschool.common;import javax.persistence.Entity; import javax.persistence.GeneratedValu…

java内存概述

运行时数据区域 Java 虚拟机在执行 Java 程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域有各自的用途&#xff0c;以及创建和销毁的时间&#xff0c;有的区域随着虚拟机进程的启 动而一直存在&#xff0c;有些区域则是依赖用户线程的启动和结束而建立和…

charls基于夜神模拟器抓取安卓7.0应用程序https请求

charls基于夜神模拟器抓取安卓7.0应用程序https请求 1、安装charls&#xff08;安装步骤这里就不详细说了&#xff09;2、下载证书&#xff08;证书后缀名 xx.pem&#xff09;3、使用git bash生成证书hash4、上传证书到安卓的系统证书目录下&#xff08;夜神模拟器方案&#xf…

C++速通LeetCode简单第9题-二叉树的最大深度

深度优先算法递归&#xff1a; /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : val(x), left(nullptr), right…

屏幕缺陷检测-目标检测数据集(包括VOC格式、YOLO格式)

屏幕缺陷检测-目标检测数据集&#xff08;包括VOC格式、YOLO格式&#xff09; 数据集&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1mb83CzAAOkvMZ_LS9Alt8w?pwdagi6 提取码&#xff1a;agi6 数据集信息介绍&#xff1a; 共有 3789 张图像和一一对应的标注文件 标…

RPC远程调用的序列化框架

序列化框架对比&#xff1a; 一、Java Serialiazer 字段serialVersionUID的作用是为了在序列化时保持版本的兼容性&#xff0c;即版本升级时反序列化仍保持对象的唯一性。 //序列化 ByteArrayOutputStream bout new ByteArrayOutputStream(); ObjectOutoutStream out new O…

【Kubernetes笔记】为什么DNS解析会超时?

【Kubernetes笔记】为什么DNS解析会超时&#xff1f; 目录 1 问题背景2 产生后续的问题3 DNS 负缓存工作原理&#xff1a;4 如何解决和缓解 DNS 负缓存 4.1 减小负缓存 TTL4.2 重试机制4.3 减少 Pod 的频繁重启或调度4.4 使用 Headless Service4.5 手动刷新 DNS 缓存 5 总结 …

苹果cms多语言插件,插件配置前端默认语言采集语言等

苹果CMS&#xff08;maccmscn&#xff09;是一款功能强大的内容管理系统&#xff0c;广泛应用于视频网站和其他内容发布平台。为了满足全球用户的需求&#xff0c;苹果CMS支持多语言插件&#xff0c;使得网站能够方便地提供多语言版本。以下是关于苹果CMS多语言插件的详细介绍&…

网络原理2-网络层与数据链路层

目录 网络层数据链路层 网络层 网络层做的工作&#xff1a; 1、地址管理–>IP地址 2、路由选择–>数据包传输的路径规划 网络层主要的协议就是IP协议 IP协议的报头结构&#xff1a; 4位版本&#xff1a; 有两个取值&#xff0c;4表示IPv4&#xff0c;6表示IPv6&am…

关于网站ERR_TOO_MANY_REDIRECTS错误的修改办法

今天网站因为证书到期&#xff0c;七牛云的报错一直ERR_TOO_MANY_REDIRECTS&#xff0c;302重定向次数过多了&#xff0c;连后台都进不去 多次清除cookie未果&#xff0c;终于找到了原因&#xff1a;设置了太多重定向&#xff0c; 1.http强制跳转 2.宝塔后台设置了跳转 3.域…

备战软考Day02-数据结构与算法

1.基本概念与三要素 1.什么是数据 数据是信息的载体&#xff0c;是描述客观事物属性的数、字符及所有能输入到计算机中并被计算机程序识别和处理的符号的集合。数据是计算机程序加工的原料。 2.数据元素、数据项 数据元素是数据的基本单位&#xff0c;通常作为一个整体进行…

树莓派Pico2(RP2350)开发环境搭建

树莓派Pico2(RP2350)开发环境搭建 文章目录 树莓派Pico2(RP2350)开发环境搭建1、RP2350介绍2、开发环境搭建3、工程编译4、固件下载Raspberry Pi再次通过推出RP2350 MCU突破了微控制器设计的界限。这款微控制器是之前RP2040的重大升级,带来了更强大的性能、高级安全功能,…

基于SSM的二手车管理系统的设计与实现 (含源码+sql+视频导入教程)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 基于SSM的二手车管理系统4拥有三种角色 管理员&#xff1a;订单管理、在售车辆管理、下架车辆管理、品牌管理、分类管理、推荐管理、统计等 商家&#xff1a;登录注册、添加/下架/删除车辆…