c++ 实现二叉搜索树

二叉搜索树的概念

二叉搜索树 (BST,Binary Search Tree),也称二叉排序树或二叉查找树。它要么是一颗空树,要么是满足以下性质的二叉树:

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

在这里插入图片描述

如上图,左侧的二叉树不是一颗二叉搜索树,右侧的二叉树是一颗二叉搜索树。

**为什么叫二叉搜索树(二叉查找树)呢?**因为二叉搜索树擅长搜索和查找。如下图的二叉搜索树,我们要查找 10,首先与根节点比较,10 大于 9,那么就去他的右子树查找。右子树的根节点为 13 大于 10,那么就去他的左子树查找。左子树的根节点为 10 刚好等于 10,查找成功!如果节点不存在那么就是查找到树的叶节点还没有找到目标节点。

在这里插入图片描述

理想状态下,二叉搜索树查找的时间复杂度为:O(logN)。但是理想很丰满,现实很骨干。有一种情况能使得二叉搜索树变成链表,从而使得查找的时间复杂度变为 O(N)。

如下图,当依次向一颗空的二叉搜索树中插入有序的节点。那么这颗二叉搜索树就会变成链表。

在这里插入图片描述

**为什么叫二叉排序树呢?**那是因为一颗二叉搜索树的中序遍历的结果是有序的!中序遍历就是在递归遍历二叉树的时候先访问左子树,在访问根节点,最后才是右子树。不会中序遍历不要紧,等我们实现了一颗二叉搜索树,中序遍历一次你就知道他为啥叫二叉排序树了!

二叉搜索树的实现

二叉搜索树的基本结构

定义二叉搜索树的基本结构:

  • 首先,我们需要定义一个节点的类,表示二叉树的一个节点,成员变量就是左子树的节点指针,右子树的节点指针以及当前节点存储的值。构造函数的话,就是传入一个 key 用来初始化节点的 _key 就行啦!指针全部初始化为 nullptr
  • 然后就要定义二叉搜索树的类啦,里面会封装各种操作的函数。至于成员变量,当然就是根节点的指针啦!至于构造函数,一开始是一颗空的二叉搜索树嘛,将根节点的指针初始化为 nullptr 就行啦!
template<class K>
struct BSTNode
{BSTNode<K>* _left;BSTNode<K>* _right;K _key;BSTNode(const K& key):_key(key),_left(nullptr),_right(nullptr){}
};template<class K>
class BSTree
{typedef BSTNode<K> Node;
public:BSTree():_root(nullptr){}private:Node* _root;
};

bool insert(const K& key)

在我们实现的二叉搜索树中,一颗树中是没有相同节点的。

插入的过程其实很简单呢?将待插入的节点 key 与根节点的 _key 作比较:

  • 如果待插入节点的 key 大于根节点的 _key,那么继续与右子树根节点的 _key 比较。
  • 如果带插入节点的 key 小于根节点的 _key,那么继续与左子树根节点的 _key 比较。
  • 如果遇到根节点为 nullptr,那么直接插入这个节点,从而完成二叉搜索树的插入操作。

在这里插入图片描述

bool insert(const K& key)
{Node* newNode = new Node(key); //构造新插入的节点if(_root == nullptr) //如果是一颗空的二叉搜索树,修改根节点就行了{_root = newNode;}else{Node* cur = _root; //新插入的节点每次都与 cur 比较,判断其插入位置Node* parent = nullptr; //记录上父节点,方便最后的插入while(cur){if(key > cur->_key) //key 大于 cur->_key 去右子树{parent = cur;cur = cur->_right;}else if(key < cur->_key) //key 小于 cur->_key 去左子树子树{parent = cur;cur = cur->_left;}else //相等的情况不存在,我们规定二叉搜索树中不存在 _key 值相同的节点,插入失败{return false; }}//确定插入的位置if(key > parent->_key)parent->_right = newNode;elseparent->_left = newNode;}//插入成功return true;
}

为了方便测试插入的结果是否正确,我们需要写一个中序遍历的函数,中序遍历在 C 语言阶段都是写过了的!忘记了的 uu 可以去复习复习。

C语言数据结构初阶(10)----二叉树的实现-CSDN博客

写成成员函数,通过类的对象调用,我们需要传参根节点的指针,但是这个成员变量是私有的!外面拿不到,你可以写一个函数获取根节点的指针,但是这里会有一个更加优雅的写法:

void inorder()
{_inorder(_root);cout << endl;
}void _inorder(Node* root)
{if(root == nullptr)return;_inorder(root->_left);cout << root->_key << " ";_inorder(root->_right);
}

中序遍历写好了,就方便我们测试一颗二叉树是不是二叉搜索树啦!二叉搜索树的中序遍历是有序的,中序遍历有序的树也是二叉搜索树。

#include<iostream>
using namespace std;
#include"BSTree.h"int main()
{BSTree<int> bst;int a[] = {8, 3, 1, 10, 6, 4, 7, 14, 13};for(auto e : a)bst.insert(e);bst.inorder();return 0;
}

在这里插入图片描述

我们看到中序遍历的结果的确是有序的呢!那我们的插入函数就是没有问题的!

bool find(const K& key)

find 写起来比 insert 还简单哈!

  • 如果待查找的 key 比根节点的 _key 大,那么就去右子树查找。
  • 如果待查找的 key 比根节点的 _key 小,那么就去左子树查找。
  • 如果待查找的 key 与根节点的 _key 相等,那么查找成功。
  • 如果根节点为 nullptr 还没有查找成功,那么查找失败。
bool find(const K& key)
{Node* cur = _root;while(cur){if(key > cur->_key)cur = cur->_right;else if(key < cur->_key)cur = cur->_left;elsereturn true;}return false;
}

void erase(const K& key)

erase 接口是二叉搜索树中最难实现的接口呢!

删除二叉搜索树中的节点可以分为以下情况:

  • 如果删除节点的度为 0,即删除的节点没有左右孩子。那么直接删除这个节点,然后改变父节点指针的指向就可以啦!

    在这里插入图片描述

  • 如果删除节点的度为 1,即删除的节点有一个孩子。那么,删除这个节点之后,令父节点相应的指针指向该删除节点的孩子即可。

    什么是父节点的相应指针?

    • 如果待删除的节点是其父节点的右孩子,那么删除这个节点之后,令父节点的 _right 指针指向该删除节点的孩子即可。
    • 如果待删除的节点是其父节点的左孩子,那么删除这个节点之后,令父节点的 _left 指针指向该删除节点的孩子即可。

    在这里插入图片描述

  • 如果待删除节点的度为 2,即待删除的节点有两个孩子。此时我们选择使用替换法,即选择待删除节点的左右子树中的某一个节点来替代待删除节点的位置,最后删除那个被选择用来替代删除节点的节点即可!

    选择哪一个节点来替代待删除的节点呢?选择的依据就是,替代之后的树应满足二叉搜索树嘛!因此就会有两种选择的方式

    • 选择待删除节点的左子树中最大的那个节点。

    • 选择待删除节点的右子树中最小的那个节点。

      在这里插入图片描述

    例如:如上图,我们要删除 3 这个节点,可以选择左子树中最大的节点 1 来代替 3,或者选择右子树中最小的节点 4 来代替 3。这两种选法均可使得删除后的二叉树满足二叉搜索树的性质。

    在这里插入图片描述

    替换之后呢?要删除的节点的特性要么满足情况 1,要么满足情况 2。就很好办啦!

    怎么找到二叉搜索树中的最大节点与最小节点呢?

    • 二叉搜索树的最大节点位于整棵树的最右侧。
    • 二叉搜索树的最小节点位于整棵树的最左侧。

其实经过仔细的观察,我们发现第一种情况和第二种情况是可以合并的!第一种情况是删除后父节点指针置空;第二种情况是删除后,父节点指向不为空的那个节点!

那么合并之后就是:如果待删除的节点的左孩子为空,那么令父节点指向右孩子就可以啦,否则,令父节点指向左孩子。或者如果待删除的节点的右孩子为空,那么令父节点指向左孩子,否者指向右孩子。

一定要看代码中的注释哦!还有一部分的细节在注释里面提到了。

bool erase(const K& key)
{Node* prev = nullptr;Node* cur = _root;//找到要被删除的那个节点while(cur){if(key > cur->_key){prev = cur;cur = cur->_right;}else if(key < cur->_key){prev = cur;cur = cur->_left;}else //找到了那个要被删除的节点{   //左子树为空,链接右子树if(cur->_left == nullptr){//这里是删除根节点的特殊情况if(cur == _root)_root = cur->_right;else{//确定删除的节点位于其父节点的哪个位置if(prev->_right == cur)prev->_right = cur->_right;elseprev->_left = cur->_right;}}else if(cur->_right == nullptr) //右子树为空链接左子树{//处理删除根节点的特殊情况if(cur == _root)_root = cur->_left;else{//确定删除的节点位于其父节点的哪个位置if(prev->_right == cur)prev->_right = cur->_left;elseprev->_left = cur->_left;}}else{//找到左子树中较大的那个节点,作为替换的节点Node* leftMax = cur->_left;Node* parent = cur;while(leftMax->_right){parent = leftMax;leftMax = leftMax->_right;}//交换swap(leftMax->_key, cur->_key);//处理特殊情况,左子树的最大节点不一定是其父节点的右孩子//当删除的节点的左孩子就是左子树的最大值,这就是那个特殊情况if(parent->_left == leftMax){parent->_left = leftMax->_left;}else{parent->_right = leftMax->_left;}cur = leftMax;}//删除节点delete cur;return true;}   }return false;
}

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

消息中间件——RabbitMQ(二)各大主流消息中间件综合对比介绍!

前言 消息队列已经逐渐成为企业IT系统内部通信的核心手段。它具有低耦合、可靠投递、广播、流量控制、最终一致性等一系列功能&#xff0c;成为异步RPC的主要手段之一。当今市面上有很多主流的消息中间件&#xff0c;如老牌的ActiveMQ、RabbitMQ&#xff0c;炙手可热的Kafka&a…

C++引用概述

变量名实质上是一段连续存储空间的别名&#xff0c;是一个标号(门牌号)&#xff0c;程序中通过变量来申请并命 名内存空间&#xff0c;通过变量的名字可以使用存储空间。引用是 C中新增加的概念&#xff0c;引用可以看作 一个已定义变量的别名。 引用的语法&#xff1a; Type&…

使用 PyTorch 构建自定义 GPT

一、介绍 介绍大模型&#xff0c;首先考虑一下使用 ChatGPT、Bing Chat 或 Bard 。您是否想过拥有自己的 ChatGPT 会是什么样子&#xff1f;想象一下创建自己的 GPT 模型的兴奋程度。这确实是一种难以置信的感觉&#xff01; 为了开始构建自定义 GPT 的旅程&#xff0c;让我们仔…

FPGA_状态机工作原理

FPGA_状态机介绍和工作原理 状态机工作原理Mealy 状态机模型Moore 状态机模型状态机描述方式代码格式 总结 状态机工作原理 状态机全称是有限状态机&#xff08;Finite State Machine、FSM&#xff09;&#xff0c;是表示有限个状态以及在这些状态之间的转移和动作等行为的数学…

AI大模型时代网络安全攻防对抗升级,瑞数信息变革“下一代应用与数据安全”

AI与大模型技术加速普及&#xff0c;安全领域也在以创新视角聚焦下一代应用安全WAAP变革&#xff0c;拓展新一代数据安全领域。近日瑞数信息重磅发布了瑞数全新API扫描器、API安全审计、数据安全检测与应急响应系统及分布式数据库备份系统四大新品。此次发布在延续瑞数信息Bot自…

C语言跟着郝斌学到指针,MDK搭建了,为什么越学越不懂?

今日话题&#xff0c;一学生说C语言跟着郝斌学到指针&#xff0c;MDK搭建了&#xff0c;为什么越学越不懂&#xff1f;在学习STM32时&#xff0c;熟练使用库函数是非常关键的一步。我最初使用了野火的教材&#xff0c;虽然内容详尽&#xff0c;但对于初学者来说可能显得有些冗长…

C#中只能在.NetFramework下使用LINQtoSQL不要在.net 下使用

目录 一、在net7.0下无法实现LINQtoSQL 1.VS上建立数据库连接 2.VS上创建LINQtoSQL 二、在.NetFramework4.8下成功实现LINQtoSQL 1.VS上建立数据库连接 2.VS上创建LINQtoSQL 三、结论 四、理由 本文是个人观点&#xff0c;因为我百般努力在.net7.0下无法实现LINQtoSQL的…

Object转List<>,转List<Map<>>

这样就不会局限在转换到List<Map<String,Object>>这一种类型上了.可以转换成List<Map<String,V>>上等,进行泛型转换虽然多了一个参数,但是可以重载啊注: 感觉field.get(key) 这里处理的不是很好,如果有更好的办法可以留言 public static <K, V> …

医药专利查询网站都有哪些?哪些最值得推荐?

药物专利信息检索是药物研发前期不可或缺的一步&#xff0c;通过对国内外药物专利技术的了解可以帮助医药企业有效降低药物研发过程中的风险。(#药品专利号查询网站&医药专利号查询网站#) 但因各国药物专利信息网站的独立性且药物名称的不统一&#xff0c;导致容易出现专利…

VS2022 打包WPF安装程序最新教程(图文详解)

文章目录 前言一、安装打包Installer插件1、单独安装2、VS中在线安装二、使用步骤1、创建安装项目2、安装项目主界面3、添加项目输出4、添加快捷方式图标5、添加卸载项目a、新建项目b、添加项目输出c、创建快捷方式6、给快捷方式添加图标a、在Resource文件夹中添加图标文件b、选…

Zeebe 微服务编排引擎 入门

相关阅读: linux 安装 zeebe: Zeebe学习(一)——Linux下安装zeebe以及快速入门_互联网集市 Zeebe是一个用于微服务编排的工作流引擎。 这篇文章将帮助你确切地了解什么是Zeebe以及它如何可能与你相关。我们将简要介绍Zeebe以及它所解决的问题,然后再进行更详细的介绍。…

终于有人把VMware虚拟机三种网络模式讲清楚了!

你们好&#xff0c;我的网工朋友。 前段时间VMware更新了&#xff0c;你用上最新版了吗&#xff1f; 有几个网工朋友留言说&#xff0c;在操作中遇到过各种各样的问题。比如说由于公司服务器重启导致出现下面的问题&#xff1a; 在Xshell里连接虚拟机映射时连接失败&#xf…

人工智能基础_机器学习016_BGD批量梯度下降求解多元一次方程_使用SGD随机梯度下降计算一元一次方程---人工智能工作笔记0056

然后上面我们用BGD计算了一元一次方程,那么现在我们使用BGD来进行计算多元一次方程 对多元一次方程进行批量梯度下降. import numpy as np X = np.random.rand(100,8) 首先因为是8元一次方程,我们要生成100行8列的X的数据对应x1到x8 w = np.random.randint(1,10,size = (8…

linux jdk配置

1.下载jdk &#xff0c;以jdk1.8为例子 Java Downloads | Oracle JDK 8 Update Release Notes (oracle.com) 2.配置环境变量 1.下载相关jdk版本&#xff0c;执行以下命令安装jdk tar -zxvf jdk-8u144-linux-x64.tar.gz 2.编辑命令 vi /etc/profile 3.在最后加入下面配置 e…

最新Ai智能创作系统源码V3.0,AI绘画系统/支持GPT联网提问/支持Prompt应用+搭建部署教程

一、AI创作系统 SparkAi创作系统是基于OpenAI很火的ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如…

【已解决】PPT不能转换成PDF文档怎么办?

PPT可以转换成PDF文档&#xff0c;只需要点击PPT菜单页面中的【文件】选项&#xff0c;再点击【导出】即可转换&#xff0c;如果转换时发现【导出】选项不可选&#xff0c;无法完成转换怎么办&#xff1f;以下3种方法可以试试&#xff01; 出现上面这种情况&#xff0c;我们可以…

【计算机网络】网络层——IP

目录 1.概念2.协议格式3.网络划分ip组成IP地址分类IP地址数量私网IP和公网IP子网掩码路由 1.概念 引入 应用层http协议是进行构建和解析请求request和响应response。 传输层的TCP/UDP协议是不提供数据的运输。传输层是为数据传输指定规则。但是&#xff0c;UDP协议并不保证数…

Web前端自动化测试Cypress实践总结

本文主要首先主要介绍了什么是自动化测试&#xff0c;接着对常用的自动化测试框架进行了对比分析&#xff0c;最后&#xff0c;介绍了如果将自动化测试框架Cypress运用在项目中。 一、自动化测试概述 为了保障软件质量&#xff0c;并减少重复性的测试工作&#xff0c;自动化测…

Python 文件处理:完整指南,一文够了

Python 是一种流行的解释型动态类型编程语言&#xff0c;用于构建 Web 服务、桌面应用程序、自动化脚本和机器学习项目。程序员在使用基于 Python 的软件项目时&#xff0c;通常必须访问操作系统的文件系统。 例如&#xff0c;我们使用文本文件作为输入&#xff0c;编写文本文…

ERP集成WMS仓储管理系统提升仓储效率

随着企业业务规模的不断扩张&#xff0c;仓储管理逐渐凸显其重要性。传统的仓储管理方式&#xff0c;随着业务量的增长&#xff0c;可能带来种种问题&#xff0c;如数据不准确、效率低下、成本增高等。在这样的背景下&#xff0c;ERP集成WMS仓储管理系统的出现成为企业的及时雨…