二叉搜索树的实现[C++]

文章目录

  • 搜索二叉树
    • 概念
    • 二叉搜索树的功能
      • 查找
  • 实现搜索二叉树
    • 节点的定义
    • 建立搜索二叉树
    • 接口
      • 插入
      • 搜索
      • 打印
      • 删除
  • 总结

今天本堂主来一起讨论下什么是搜索二叉树,和如何实现二叉搜索树

搜索二叉树

那么二叉搜索树似乎如何实现搜索呢?二叉搜索树和普通二叉树有什么不同呢?

概念

搜索二叉树又可以称作二叉搜索树或二叉排序树。
搜索二叉树需要满足几个条件或者说要具备几个性质:

  • 1.若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  • 2.若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  • 3.它的左右子树也分别为二叉搜索树
    在这里插入图片描述

二叉搜索树的功能

查找

二叉搜索树的查找:

  • 从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找。
  • 最多查找高度次,走到到空,还没找到,这个值不存在。

这样在理想情况下时间复杂度是O(logn),而极端情况如下图时间复杂度还是O(n²)

在这里插入图片描述

实现搜索二叉树

那么如何实现搜索二叉树呢?
在这里插入图片描述
咱们可以按照这个思路去实现

节点的定义

定义节点的时候二叉树包括:本身值,左节点,右节点
那么我们就可以这样来定义节点

template<class K>
struct BSTNode
{K _key;BSTNode<K>* _left;BSTNode<K>* _right;BSTNode(const K& key):_key(key),_left(nullptr),_right(nullptr){}
};

这样我们就定义了一个二叉搜索树的节点结构体,构造函数则是用于创建节点并初始化建值,左右指针默认为空

建立搜索二叉树

那么接下来我们就进行声明根节点和初始化根节点为我们后面实现搜索二叉树的功能做铺垫

template<class K>
class BSTree
{typedef BSTNode<K> Node;\*...*\
private:Node* _root = nullptr;
};

声明根节点:我们声明了一个名为 _root 的指针,该指针指向 Node 类型的对象。这里的 Node 是 BSTNode 的类型别名。

初始化根节点:通过将 _root 初始化为 nullptr,我们是在创建一个空的搜索二叉树,为后面做铺垫。

接口

万事俱备那么我们就来实现搜索二叉树的功能吧

插入

首先一定是向空的搜索二叉树中插入数。

很简单我们先在BSTree的类里面定义一个Insert的函数

//插入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 if (parent->_key > key){parent->_left = cur;}return true;}
  • 我们先判断_root如果为空那么我们定义一个新新节点连接到根节点,并给新节点赋值
  • 其次如果树不为空,遍历树来找到正确的插入位置(我这里用的双指针防止迷路)
  • 如果找到和键值相同的节点则不插入
  • 符合搜索二叉树的性质加入新节点

搜索

有了插入的铺垫我们搜索的函数在遍历查找树这里就可以CV下

//查找bool Find(const K& key){Node* cur = _root;while (cur){if (cur->_key == key){return true;}else if (cur->_key < key){cur = cur->_right;}else{cur = cur->_left;}}return false;}

不同的是在插入的时候找到和键值相同的值是返回false 而在搜索函数找到键值返回true证明找到了,而没找到值的时候才返回false

打印

那么打印这里就正常写就可以,但是会有一点点小问题
在我正常写打印函数之后,因为_root是私有的只有内部能用所以我用不了

	void InOrder(){if (root == nullptr){return;}InOrder(root->_left);cout << root->_key << " ";InOrder(root->_right);}

在这里我就进行了些调整

	//打印void InOrder(){_InOrder(_root);cout << endl;}
private://打印的子函数void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);}

这样我用个子函数之后再在打印函数中调用子函数就可以了

删除

相较前两个删除是比较麻烦的

看长度可知⬇

	//删除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{//0-1个孩子if (cur->_left == nullptr){if (parent == nullptr){_root = cur->_right;}else{if (parent->_left == cur){parent->_left = cur->_right;}else{parent->_right = cur->_right;}}delete cur;return true;}else if (cur->_right == nullptr){if (parent == nullptr){_root = cur->_left;}else{if (parent->_left == cur){parent->_left == cur->_left;}else{parent->_right = cur->_left;}}delete cur;return true;}//两个孩子else{Node* rightMinP = cur;Node* rightMin = cur->_right;while (rightMin->_left){rightMinP = rightMin;rightMin = rightMin->_left;}cur->_key = rightMin->_key;if (rightMinP->_left == rightMin){rightMinP->_left = rightMin->_right;}else{rightMinP->_right = rightMin->_right;}delete rightMin;return true;}}}return false;}

那么删除这里我在 BSTree 类中定义成员函数 Erase,用于删除搜索二叉树中具有特定键值 key 的节点。

  1. 定义函数bool Erase(const K& key),这是一个返回 bool 类型的函数,它接受一个键值 key 作为参数。
  2. 查找要删除的节点
    • 初始化两个指针 parentcur,分别用于跟踪父节点和当前节点。
    • 进入一个循环,直到找到要删除的节点或者当前节点为 nullptr
    • 在循环中,通过比较 cur->_keykey 来确定是否继续向左或向右遍历。
  3. 删除节点的逻辑
    • 如果找到要删除的节点,则根据节点是否有子节点或子节点的数量来决定如何删除节点。
    • 如果节点只有一个子节点(要么是左子节点,要么是右子节点),则直接将父节点的相应指针指向该子节点。
    • 如果节点有两个子节点,则需要找到右子树中的最小节点(即右子树的最左下角的节点),将其值复制到当前节点,然后删除右子树中的最小节点。
  4. 释放内存
    • 在删除节点后,如果节点不再被引用,则释放其内存。
  5. 返回结果
    • 如果成功删除节点,返回 true
    • 如果循环结束而没有找到要删除的节点,返回 false
      这段代码实现了搜索二叉树中键值的删除操作,它通过递归或迭代的方式找到要删除的节点,并确保在删除节点后树仍然保持搜索二叉树的性质。

总结

那么以上就是搜索二叉树的说明和实现,能给我点点赞吗?

完整代码:

#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){}
};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 if (parent->_key > key){parent->_left = cur;}return true;}//查找bool Find(const K& key){Node* cur = _root;while (cur){if (cur->_key == key){return true;}else if (cur->_key < key){cur = cur->_right;}else{cur = cur->_left;}}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{//0-1个孩子if (cur->_left == nullptr){if (parent == nullptr){_root = cur->_right;}else{if (parent->_left == cur){parent->_left = cur->_right;}else{parent->_right = cur->_right;}}delete cur;return true;}else if (cur->_right == nullptr){if (parent == nullptr){_root = cur->_left;}else{if (parent->_left == cur){parent->_left == cur->_left;}else{parent->_right = cur->_left;}}delete cur;return true;}//两个孩子else{//右子树的最大节点作为替代节点/*Node* rightMinP = nullptr;Node* rightMin = cur->_right;while (rightMin->_left){rightMinP = rightMin;rightMin = rightMin->_left;}cur->_key = rightMin->_key;rightMinP->_left = rightMinP->_right;delete rightMin;return true;*/Node* rightMinP = cur;Node* rightMin = cur->_right;while (rightMin->_left){rightMinP = rightMin;rightMin = rightMin->_left;}cur->_key = rightMin->_key;if (rightMinP->_left == rightMin){rightMinP->_left = rightMin->_right;}else{rightMinP->_right = rightMin->_right;}delete rightMin;return true;}}}return false;}//打印void InOrder(){_InOrder(_root);cout << endl;}
private://打印的子函数void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);}private:Node* _root = nullptr;
};

封面图
在这里插入图片描述

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

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

相关文章

linux 之时间子系统(八):hrtime 的实现机制

一、hrtimer 概述 在Linux内核中已经存在了一个管理定时器的通用框架。不过它也有很多不足&#xff0c;最大的问题是其精度不是很高。哪怕底层的定时事件设备精度再高&#xff0c;定时器层的分辨率只能达到Tick级别&#xff0c;按照内核配置选项的不同&#xff0c;在100Hz到10…

数据库管理1

数据库管理 数据库运维。 sql语句 数据库用来增删改查的语句 备份 数据库的数据进行备份 主从复制&#xff0c;读写分离&#xff0c;高可用。 数据库的概念和相关的语法和规范&#xff1a; 数据库&#xff1a;组织&#xff0c;存储&#xff0c;管理数据的仓库。 数据库的管理系…

Vue3中的ref函数

在Vue 3中&#xff0c;ref函数是用来创建一个响应式的基本数据类型的函数。通过ref函数创建的变量会被自动包装成一个响应式对象&#xff0c;当这个变量的值发生变化时&#xff0c;相关的视图会自动更新。 下面是ref函数的基本用法和特点&#xff1a; 基本用法&#xff1a; …

一篇文章让你掌握计算网络的HTTP协议!!

HTTP 浏览器的服务原理http协议webTCP/IP协议族TCP/IP协议族分层应用层传输层网络层链路层数据包的封装过程HTTP数据传输过程传输层——TCP三次握手第一次握手第二次握手第三次握手三次握手的目的DNS域名解析HTTP完整事务处理过程HTTP协议的特点支持客户/服务器模式简短快速灵活…

钡铼Modbus TCP耦合器BL200实现现场设备与SCADA无缝对接

前言 深圳钡铼技术推出的Modbus TCP耦合器为SCADA系统与现场设备之间的连接提供了强大而灵活的解决方案&#xff0c;它不仅简化了设备接入的过程&#xff0c;还提升了数据传输的效率和可靠性&#xff0c;是工业自动化项目中不可或缺的关键设备。本文将从Modbus TC、SCADA的简要…

Apache网页优化(企业网站结构部署与优化)

本章结构 一、Apache网页优化 在使用 Apache 作为 Web 服务器的过程中&#xff0c;只有对 Apache 服务器进行适当的优化配置&#xff0c;才能让 Apache 发挥出更好的性能。反过来说&#xff0c;如果 Apache 的配置非常糟糕&#xff0c;Apache可能无法正常为我们服务。因此&…

Java8的新特性

Java8的新特性 一、函数式接口1、Java内置的函数式接口 二、Lambda表达式1、Lambda作用2、语法3、Lambda表达式的六种使用3.1、抽象方法&#xff1a;无参、无返回值3.2、抽象方法&#xff1a;需要传一个参数、无返回值3.3、抽象方法&#xff1a;需要传一个参数&#xff08;类型…

Linux网络攻击防御高级指南:结合 iftop 和 iptables

Linux网络攻击防御高级指南&#xff1a;结合 iftop 和 iptables&#xff08;含规则审查&#xff09; 网络攻击&#xff0c;特别是DDoS&#xff08;分布式拒绝服务&#xff09;攻击&#xff0c;可能导致服务器带宽资源枯竭&#xff0c;严重影响服务稳定性。本高级指南将引导您如…

【笔记】FLOPs 和 FLOPS: 前者(FLOPs) 越大,说明模型的计算复杂度越高;后者(FLOPS)越大,说明硬件的计算速度越快

FLOPs&#xff08;Floating Point Operations&#xff09;&#xff1a;这是指一个模型在一次前向传播中需要进行的浮点运算次数&#xff0c;是一个衡量模型计算复杂度的指标。FLOPs越大&#xff0c;说明模型的计算复杂度越高。 FLOPS&#xff08;Floating Point Operations Pe…

【iOS】OC类与对象的本质分析

目录 前言clang常用命令对象本质探索属性的本质对象的内存大小isa 指针探究 前言 OC 代码的底层实现都是 C/C代码&#xff0c;OC 的对象都是基于 C/C 的数据结构实现的&#xff0c;实际 OC 对象的本质就是结构体&#xff0c;那到底是一个怎样的结构体呢&#xff1f; clang常用…

IDEA实现SpringBoot项目的自打包自发布自部署

目录 前言 正文 操作背景 自发布 自部署 尾声 &#x1f52d; Hi,I’m Pleasure1234&#x1f331; I’m currently learning Vue.js,SpringBoot,Computer Security and so on.&#x1f46f; I’m studying in University of Nottingham Ningbo China&#x1f4eb; You can reach…

社交媒体安全:个人信息泄露与社交工程攻击的防范

社交媒体安全&#xff1a;个人信息泄露与社交工程攻击的防范 社交媒体平台的普及带来了个人信息泄露和社交工程攻击的风险。个人信息泄露可能导致身份盗窃、诈骗等问题&#xff0c;而社交工程攻击则利用心理学和社会学手段欺骗用户&#xff0c;获取敏感信息或系统访问权限。 个…

用户excel对CAN进行图形化展示

在Excel中对CAN数据进行图形化展示&#xff0c;用户可以通过以下几个步骤来实现&#xff1a; 一、数据准备 导出CAN数据&#xff1a;首先&#xff0c;需要将CAN数据从CAN分析工具或设备中导出为Excel支持的格式&#xff08;通常是.xlsx或.csv&#xff09;。大多数CAN系列工具软…

LVS的DR模式

一、DR模式 DR模式&#xff1a;直接路由模式 1.1 DR模式的工作方式 调度器在整个LVS集群当中是最重要的。 在NAT模式下&#xff0c;调度器负责接受请求&#xff0c;同时根据负载均衡的算法转发流量&#xff0c;响应给客户端。 DR模式下&#xff0c;调度器依然负责接受请求…

qt 创建一个左侧边线拖拽的矩形

1.概要 2.代码 2.1 代码第一版 在Qt中&#xff0c;要创建一个可以向左侧拖拽边线的矩形&#xff0c;你需要自定义一个QGraphicsRectItem的子类&#xff0c;并重写其事件处理函数来响应鼠标的拖拽动作。以下是一个简单的实现示例&#xff1a; #include <QApplication>…

设计模式——装饰者模式

设计模式——装饰者模式 1.问题1.1 方案一1.2 方案二 2.装饰者模式2.1 基本介绍2.2 结构2.3 代码实现 3.小结 1.问题 咖啡订单项目&#xff1a; 咖啡种类/单品咖啡:Espresso(意大利浓咖啡)、ShortBlack、LongBlack(美式咖啡)、Decaf(无因咖啡)调料:Milk、Soy(豆浆)、Chocolat…

「Conda」在Linux系统中安装Conda环境管理器

在Linux系统中安装Conda环境管理器是一个相对简单的过程。 1. 准备工作 确保你的Linux系统已经更新到最新版本,并安装了基本的开发工具和库。打开终端,执行以下命令: sudo apt-get update sudo apt-get upgrade sudo apt-get install build-essential2. 安装Miniconda或An…

GB35114控制信令认证流程

GB35114控制信令认证说明&#xff1a; 注册成功后,信令发送方与信令接收方进行交互时,采用基于带密钥的杂凑方式保障信令来源安 全&#xff61;对除REGISTER消息以外的消息做带密钥的杂凑&#xff61;启用Date字段,扩展信令消息头域,在头域中 增加 Note 字 段 (值 为 Digest…

项目经理到底要不要考PMP?

在接待PMP学员中我惊讶地发现&#xff0c;不仅是项目经理&#xff0c;连开发、测试、产品、运营、销售、甚至财务团队的朋友们也都在积极备考。他们考证的原因主要有这几点&#xff1a; 1&#xff0c;职业发展&#xff1a;希望在职业生涯中晋升或转型到项目管理角色的朋友来说…