搜索二叉树

目录

搜索二叉树的概念

二叉搜索树的遍历

二叉搜索树的模拟实现

Find查找函数

 Insert插入函数

 Erase删除函数

case 1:

case 2:

待删除结点左孩子不存在,右孩子存在

 待删除结点左孩子存在,右孩子不存在

case 3:

Erase循环版本实现

中序遍历

析构函数

拷贝构造函数

赋值运算符重载

构造函数


搜索二叉树的概念

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

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

二叉搜索树的遍历

二叉树的遍历包括前序遍历,中序遍历,后序遍历,此处按照前序遍历进行展示;

前序遍历的顺序为首先遍历根结点,其次遍历左子树,最后遍历右子树;

遍历方法:

先把根拔下来,再把左子树拔下来,最后把右子树拔下来,依次排好顺序,然后再以同样的方法递归似的拔左子树,直至左子树上的结点被拔光,最后按照同样的方式拔右子树;

 前序遍历结果:8 ---> 3 ---> 1 ---> 6 ---> 4 --->7 ---> 10 ---> 14---> 13

二叉搜索树的模拟实现

实现一颗二叉搜索树,需要实现两个类,第一个是结点类,用于存放节点值、左指针、右指针;第二个类专门用于二叉搜索树的增删查改;

//结点类的实现
template <class K>
struct BinarySearchTreeNode
{typedef BinarySearchTreeNode Node;Node* _left;//指向左子树Node* _right;//指向右子树K _key;//存储当前结点数据//开辟新结点需要构造函数BinarySearchTreeNode(const K& key):_left(nullptr), _right(nullptr), _key(key){}
};

二叉搜索树的类(第二个类)用于实现搜索二叉树的增删查改;

template <class K>
class BinarySearchTree
{typedef BinarySearchTreeNode<K> Node;
public://...
private:Node* _root=nullptr;
};

Find查找函数

搜索二叉树的查找,找到了返回true,否则返回false;
思路:

  1. 定义cur指针从根结点开始,将待查找元素key与当前结点处的键值进行比较;
  2. 若key小于当前结点处的键值,则去该节点的左子树查找若key大于当前结点处的键值,则去该节点的右子树查找;
  3. 查找过程中走到结点指针为空,找不到key,返回false
  4. 若查找过程中某个结点数据与key相等,停止查找,返回true
bool Find(const K& key)
{Node* cur = _root;while (cur != nullptr){//key大于当前结点处的键值,则去该节点的右子树查找if (cur->_key < key){cur = cur->_right;}//若key小于当前结点处的键值,则去该节点的左子树查找else if (cur->_key > key){cur = cur->_left;}//查找过程中某个结点数据与key相等,停止查找,返回true;else{return true;}}//cur走空,找不到返回falsereturn false;
}

 Insert插入函数

思路: 插入的要求是插入的结点依旧维持搜索二叉树的逻辑结构

  1. 首先确定插入的位置,从根节点开始,将插入的数值与该结点处的数值相比较;
  2. 若大于该结点处的数值,前往该节点作为根节点的右子树查找;
  3. 若小于该结点处的数值,前往该节点作为根节点的左子树查找;
  4. 直至走到结点指针为空的位置,每次查找需要记录该结点的父亲结点的位置,方便建立链接关系;
bool Insert(const K& key)
{// 空树if (_root == nullptr){_root = new Node(key);return true;}//非空树//parent记录父节点位置Node* parent = nullptr;Node* cur = _root;while (cur != nullptr){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key>key){parent = cur;cur = cur->_left;}//为维护搜索二叉树逻辑结构,同一颗树不能出现相等的数值else{return false;}}//cur已经走空,开辟节点,存储数据,建立链接关系cur = new Node(key);//存储值为key的结点链接到父节点的何处?if (parent->_key > key){parent->_left = cur;}else{parent->_right = cur;}return true;
}

 Erase删除函数

搜索二叉树的删除:删除的要求是删除结点后依旧维持搜索二叉树的逻辑结构;
首先查找待删除元素是否在搜索二叉树中,不在返回false,否则返回true;

若查找到待删除元素所在的结点,删除结点,删除结点分三种情形:

  1. 删除叶子结点;
  2. 删除单孩子结点(其中又分为待删除节点处的左孩子不存在、右孩子存在或者左孩子存在、右孩不存在);
  3. 删除左右孩子皆存在的结点;

case 1:

删除叶子结点(删除13),直接释放cur所指向的空间;

case 2:

待删除结点左孩子不存在,右孩子存在

if(cur->_left==nullptr)
{parent->_right=cur->_right;delete cur;
}

if(cur->_left==nullptr)
{parent->_left=cur->_right;delete cur;
}

通过删除10与删除1,注意到一个问题,单孩子结点处的孩子结点应该链接于单孩子节点的父节点的左指针还是右指针?

需要根据单孩子结点处的键值与其父节点处的键值相比较,从而确定链接位置;

若单孩子结点处的键值大于其父节点处的键值,应该将单孩子结点处的孩子结点链接于父节点的右指针;

若单孩子结点处的键值小于其父节点处的键值,应该将单孩子结点处的孩子结点链接于父节点的左指针;

if(cur->_left==nullptr)
{if(parent->_key < cur->_key){parent->_right=cur->_right;}if(parent->_key > cur->_key){parent->_left=cur->_right;}delete cur;
}

if(cur->_left==nullptr)
{if(cur == _root){_root = cur->_right;delete cur;}
}

 待删除结点左孩子不存在,右孩子存在的所有情况分析汇总如下:

if (cur->_left == nullptr)
{if (cur == _root){_root = cur->_right;}else{if (parent->_key < cur->_key){parent->_right = cur->_right;}else{parent->_left = cur->_right;}}delete cur;return true;
}
 待删除结点左孩子存在,右孩子不存在

if(cur->_right == nullptr)
{parent->_right=cur->_left;delete cur;
}

if(cur->_right==nullptr)
{parent->_left=cur->_left;delete cur;
}

通过删除14与删除0,注意到一个问题,单孩子结点处的孩子结点应该链接于单孩子节点的父节点的左指针还是右指针?

需要根据单孩子结点处的键值与其父节点处的键值相比较,从而确定链接位置;

若单孩子结点处的键值大于其父节点处的键值,应该将单孩子结点处的孩子结点链接于父节点的右指针;

若单孩子结点处的键值小于其父节点处的键值,应该将单孩子结点处的孩子结点链接于父节点的左指针;

if(cur->_right==nullptr)
{if(parent->_key < cur->_key){parent->_right=cur->_left;}if(parent->_key > cur->_key){parent->_left=cur->_left;}delete cur;
}

if(cur->_right==nullptr)
{if(cur == _root){_root = cur->_left;delete cur;}
}

 待删除结点左孩子存在,右孩子不存在的所有情况分析汇总如下:

//待删除结点左孩子存在,右孩子不存在
else if (cur->_right == nullptr)
{if (cur == _root){_root = cur->_left;}else{if (parent->_key < cur->_key){parent->_right = cur->_left;}else{parent->_left = cur->_right;}}delete cur;return true;
}

case 3:

思路:(替换法删除)

  1. 选取待删除结点作为根结点的左子树的最大结点(左子树的最右节点)或者选取待删除结点作为根结点的右子树的最小结点(右子树的最左节点)替换当前待删除结点
  2. 假设选取右子树的最左结点作为替换结点,查找替换结点RightMin的位置,将RightMin结点处的值直接赋予cur
  3. 删除RightMin节点

注:替换结点不一定为叶子结点,但必为单孩子结点(若右子树最左结点为替换结点,则替换结点必定没有左孩子)

//寻找RightMin结点即右子树的最左结点
Node* RightMinParent = cur;
Node* RightMin = cur->_right;
while (RightMin->_left != nullptr)
{RightMinParent = RightMin;RightMin = RightMin->_left;
}
//找到RightMin结点,替换删除
cur->_key = RightMin->_key;
//链接
if (RightMinParent->_left == RightMin)//RightMin->_left==nullptr
{RightMinParent->_left = RightMin->_right;
}
if (RightMinParent->_right == RightMin)
{RightMinParent->_right = RightMin->_right;
}
delete RightMin;
return true;

Erase循环版本实现

bool Erase(const K& key)
{
Node* parent = nullptr;
Node* cur = _root;
while (cur != nullptr)
{
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->_key < cur->_key){parent->_right = cur->_right;}else{parent->_left = cur->_right;}
}
delete cur;
return true;
}
else if (cur->_right == nullptr)
{if (cur == _root){_root = cur->_left;}else{if (parent->_key < cur->_key){parent->_right = cur->_left;}else{parent->_left = cur->_right;}}delete cur;return true;
}
else
{
//寻找RightMin结点即右子树的最左结点
Node* RightMinParent = cur;
Node* RightMin = cur->_right;
while (RightMin->_left != nullptr)
{RightMinParent = RightMin;RightMin = RightMin->_left;
}
//找到RightMin结点,替换删除
cur->_key = RightMin->_key;
//链接
if (RightMinParent->_left == RightMin)//RightMin->_left==nullptr
{RightMinParent->_left = RightMin->_right;
}
if (RightMinParent->_right == RightMin)
{RightMinParent->_right = RightMin->_right;
}
delete RightMin;
return true;
}
}
}
return false;
}

中序遍历

//搜索二叉树的中序遍历(左子树-->根-->右子树)
//外部需要将_root传递给root,但是外部无法访问_root
void _InOrder(Node* root)
{if (root == nullptr){return;}_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);
}
void InOrder()
{_InOrder(_root);cout << endl;
}

析构函数

由于结点在堆区申请空间,所以需要手动释放空间;

//首先销毁左子树,其次销毁右子树,最后销毁根结点;
void Destroy(Node* root)
{if (root == nullptr)return;Destroy(root->_left);Destroy(root->_right);delete root;
}
~BinarySearchTree()
{Destroy(_root);
}

拷贝构造函数

结点指针为自定义类型,涉及浅拷贝,导致同一块空间被析构两次,需要实现深拷贝与深赋值;

BinarySearchTree<int> t1(t);

  思路:

  1.     创建一个新二叉搜索树,作为拷贝的目标树;
  2.     从始树的根节点开始,递归地复将其插入到目标树中;
  3.     对于每个节点,复制其值,并递归地复制其左子树和右子树;
  4.     返回一个拷贝后的二叉搜索树的根节点;
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;
}
BinarySearchTree(const BinarySearchTree<K>& t)
{_root = copy(t._root);
}

赋值运算符重载

//BinarySearchTree<int> t2 = t1;
BinarySearchTree<K>& operator=(const BinarySearchTree<K>& t)
{swap(_root, t._root);return *this;
}

构造函数

//构造函数
BinarySearchTree() = default;//强制生成默认构造

欢迎大家批评指正,博主会持续输出优质内容,谢谢各位观众老爷观看,码字画图不易,希望大家给个一键三连支持~ 你的支持是我创作的不竭动力~

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

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

相关文章

easyExcel 模版导出 中间数据纵向延伸,并且对指定列进行合并

想要达到的效果 引入maven引用 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.2.1</version></dependency> 按照要求创建模版 备注 : 模板注意 用{} 来表示你要用的变量 如果本…

C#使用Selenium驱动Chrome浏览器

1.Selenium库依赖安装 Selenium WebDriver是Selenium项目的一部分&#xff0c;用于模拟用户在Web应用程序中的交互操作。它支持多种浏览器&#xff0c;如Chrome、Firefox、IE等&#xff0c;且与各种编程语言&#xff08;如Java、Python、C#等&#xff09;兼容&#xff0c;具有…

RUST语言流控制语句使用示例

1.判断语句 单条件判断: let mut x128;//声明一个32位整数x512;//修改变量原来的值为新值//如果 ... 否则//判断变量x是否大于256if x>256 {println!("x>256,x{}",x);}else {println!("x<256,x{}",x);}let is_ok:bool true;//rust中不用()if i…

pytest--python的一种测试框架--接口测试

接口测试 工具&#xff1a; POSTMAN&#xff1b; 接口选择&#xff1a; 豆瓣电影&#xff0c;进制数据 POSTMAN下载&#xff1a; 1.POSTMAN官网&#xff1a;https://www.postman.com/products/&#xff1b; 2.点product选Download Postman 下载完之后双击打开就可以用的。…

【TI毫米波雷达】IWR6843AOP的官方文件资源名称BUG,选择xwr68xx还是xwr64xx,及需要注意的问题

【TI毫米波雷达】IWR6843AOP的官方文件资源名称BUG&#xff0c;选择xwr68xx还是xwr64xx&#xff0c;及需要注意的问题 文章目录 demo工程out_of_box文件调试bin文件名称需要注意的问题附录&#xff1a;结构框架雷达基本原理叙述雷达天线排列位置芯片框架Demo工程功能CCS工程导…

ETL工具-nifi干货系列 第八讲 处理器PutDatabaseRecord 写数据库(详细)

1、本节通过一个小例子来讲解下处理器PutDatabaseRecord&#xff0c;该处理器的作用是将数据写入数据库。 如下流程通过处理器GenerateFlowFile 生成数据&#xff0c;然后通过处理器JoltTransformJSON转换结构&#xff0c;最后通过处理器PutDatabaseRecord将数据写入数据库。如…

一些题目学习

1.打开文件添加helloworld public class Saier {public static void main(String[] args){String path"C:\\Users\\sjg\\Desktop\\abc.txt";String text"hello world";try {File file new File(path);FileWriter fileWriter new FileWriter(file,true);…

AI绘图:Stable Diffusion WEB UI 详细操作介绍:基础篇

接上一篇《AI绘图体验&#xff1a;Stable Diffusion本地化部署详细步骤》本地部署完了SD后&#xff0c;大家肯定想知道怎么用&#xff0c;接下来补一篇Stable Diffusion WEB UI 详细操作&#xff0c;如果大家还没有完成SD的部署&#xff0c;请参考上一篇文章进行本地化的部署。…

Emacs之解除comment-region绑定C-c C-c快捷键(一百三十四)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

【学习心得】Numpy学习指南或复习手册

本文是自己在学习Numpy过后总是遗忘的很快&#xff0c;反思后发现主要是两个原因&#xff1a; numpy的知识点很多&#xff0c;很杂乱。练习不足&#xff0c;学习过后一段时间不敲代码就会忘记。 针对这两个问题&#xff0c;我写了这篇文章。希望将numpy的知识点织成一张网&…

开源模型应用落地-chatglm3-6b模型小试-入门篇(一)

一、前言 刚开始接触AI时&#xff0c;您可能会感到困惑&#xff0c;因为面对众多开源模型的选择&#xff0c;不知道应该选择哪个模型&#xff0c;也不知道如何调用最基本的模型。但是不用担心&#xff0c;我将陪伴您一起逐步入门&#xff0c;解决这些问题。 在信息时代&#xf…

5.3.1 配置交换机 SSH 管理和端口安全

5.3.1 实验1:配置交换机基本安全和 SSH管理 1、实验目的 通过本实验可以掌握&#xff1a; 交换机基本安全配置。SSH 的工作原理和 SSH服务端和客户端的配置。 2、实验拓扑 交换机基本安全和 SSH管理实验拓扑如图所示。 交换机基本安全和 SSH管理实验拓扑 3、实验步骤 &a…

Nginx从安装到高可用实用教程!

一、Nginx安装 1、去官网http://nginx.org/下载对应的nginx包&#xff0c;推荐使用稳定版本 2、上传nginx到linux系统 3、安装依赖环境 (1)安装gcc环境 yum install gcc-c(2)安装PCRE库&#xff0c;用于解析正则表达式 yum install -y pcre pcre-devel(3)zlib压缩和解压缩…

Linux项目自动化构建工具 --- make/Makefile

文章目录 make/Makefile文件1 背景2 理解2.1 创建执行代码2.2 创建makefile文件2.3 运行make指令2.3.1 依赖关系2.3.2 依赖方法2.3.3 原理 2.4 项目清理 make/Makefile文件 1 背景 会不会写makefile&#xff0c;从一个侧面说明了一个人是否具备完成大型工程的能力一个工程中的…

鸿蒙OS开发实例:【应用事件打点】

简介 传统的日志系统里汇聚了整个设备上所有程序运行的过程流水日志&#xff0c;难以识别其中的关键信息。因此&#xff0c;应用开发者需要一种数据打点机制&#xff0c;用来评估如访问数、日活、用户操作习惯以及影响用户使用的关键因素等关键信息。 HiAppEvent是在系统层面…

分布式链路追踪与云原生可观测性

分布式链路追踪系统历史 Dapper, a Large-Scale Distributed Systems Tracing Infrastructure - Google Dapper&#xff0c;大规模分布式系统的跟踪系统大规模分布式系统的跟踪系统&#xff1a;Dapper设计给我们的启示 阿里巴巴鹰眼技术解密 - 周小帆京东云分布式链路追踪在金…

【机器学习】“强化机器学习模型:Bagging与Boosting详解“

1. 引言 在当今数据驱动的世界里&#xff0c;机器学习技术已成为解决复杂问题和提升决策制定效率的关键工具。随着数据的增长和计算能力的提升&#xff0c;传统的单一模型方法已逐渐无法满足高精度和泛化能力的双重要求。集成学习&#xff0c;作为一种结合多个学习算法以获得比…

Spring Boot中前端通过请求接口下载后端存放的Excel模板

导出工具类 package com.yutu.garden.utils;import com.baomidou.mybatisplus.core.toolkit.ObjectUtils; import org.apache.commons.io.IOUtils; import org.apache.poi.hssf.util.HSSFColor; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.slf4j.Logger;…

vue项目引入微信sdk: npm install weixin-js-sdk --save报错

网上查到要用淘宝的镜像 同事告知旧 域名&#xff1a;https://registry.npm.taobao.org/已经不能再使用 使用 npm config set registry http://registry.npmmirror.com

[力扣]根据前中序构造二叉树--详细解析

根据前中序遍历顺序构建一个二叉树 力扣练习链接 过程 总体框架 设preorder的左边界为pleft,右边界为pright[注意这里是闭区间能取到]同时设inorder的左边界为ileft,有边界为iright[同样也是可以取到的索引区间]我们生成每一个区间的树的头结点,然后向上返回,对于他的父亲结点…