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

news2024/12/23 20:34:20

文章目录

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

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

搜索二叉树

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

概念

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

  • 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.coloradmin.cn/o/1928067.html

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

相关文章

PNPM 高效入门:安装配置一本通

PNPM高效入门&#xff1a;安装配置一本通 引言Pnpm 简介安装 PNPM全局安装&#xff08;推荐&#xff09;使用 nvm&#xff08;Node Version Manager&#xff09; 配置PNPM使用PNPM管理项目初始化项目 添加依赖快速安装所有依赖查看安装的包 优化与故障排除PNPM与持续集成/持续部…

nacos 适配瀚高数据库、ARM 架构

下载nacos源码: https://github.com/alibaba/nacos/tree/2.3.1 瀚高技术文档 1、修改pom.xml 根目录nacos-all => pom.xml<dependencyManagement><dependency><groupId>com.highgo</groupId><artifactId>HgdbJdbc</artifactId><…

安全防御:防火墙基本模块

目录 一、接口 1.1 物理接口 1.2 虚拟接口 二、区域 三、模式 3.1 路由模式 3.2 透明模式 3.3 旁路检测模式 3.4 混合模式 四、安全策略 五、防火墙的状态检测和会话表技术 一、接口 1.1 物理接口 三层口 --- 可以配置IP地址的接口 二层口&#xff1a; 普通二层…

linux的学习(四):磁盘,进程,定时,软件包的相关命令

简介 关于磁盘管理&#xff0c;进程管理&#xff0c;定时任务&#xff0c;软件包管理的命令的使用 磁盘管理类命令 du du 目录名&#xff1a; 查看文件和目录占用的磁盘空间 参数&#xff1a; -h&#xff1a;可以看到大小的单位&#xff0c;g,mb-a&#xff1a;还可以看到文…

Mapboxgl 生成飞行动画GIF

更多精彩内容尽在数字孪生平台&#xff0c;关注公众号【sky的数孪技术】&#xff0c;技术交流、源码下载请添加VX&#xff1a;digital_twin123 根据两点生成动画的工具。 首先&#xff0c;找到你想要开始的视图&#xff0c;点击“设置起点视图”&#xff0c;然后调整到目的视图…

Android中RecyclerView使用详解(一)

目录 概述优点列表布局RecyclerView一、创建RecyclerView并且在布局中绑定二、实现RecyclerView单个item的布局三、给RecyclerView写一个对应的适配器Adapter1.创建自定义的ViewHolder2.继承Adapter&#xff0c;泛型使用我们自定义的ViewHolder3.重写Adapter的三个方法onCreate…

如何提取视频中的音频?提取音频的几种方法

如何提取视频中的音频&#xff1f;提取视频中的音频&#xff0c;是许多人在处理多媒体内容时常遇到的需求。这一过程不仅仅是简单地从视听媒体中抽离音频部分&#xff0c;它背后蕴含着许多技术上的挑战和创意上的可能性。通过提取音频&#xff0c;你可以更方便地利用视频中的声…

网络安全——防御(防火墙)带宽以及双机热备实验

12&#xff0c;对现有网络进行改造升级&#xff0c;将当个防火墙组网改成双机热备的组网形式&#xff0c;做负载分担模式&#xff0c;游客区和DMZ区走FW3&#xff0c;生产区和办公区的流量走FW1 13&#xff0c;办公区上网用户限制流量不超过100M&#xff0c;其中销售部人员在其…

docker镜像导出与导入

布置程序出问题了&#xff0c;拉不下来镜像 程序的配置文件里面有镜像的名字 docker pull ubuntu/squid:latest 我是本地下载了镜像&#xff0c;使用本地的镜像导出 导出镜像使用 docker 导出导入镜像 要在Docker中导出和导入镜像&#xff0c;您可以使用docker save命令来导…

《昇思25天学习打卡营第5天|10使用静态图加速》

写在前面 跳过了模型训练和保存两节&#xff0c;模型训练的例子已经学习过&#xff0c;都是重复的内容&#xff0c;不进行详细的学习了&#xff0c;除非要使用类似的函数。 因此接下来开始学习初学教程的最后一节&#xff1a;使用静态图加速&#xff0c;希望能学习到一些内容。…

【分布式系统】CephFS文件系统之MDS接口详解

目录 一.服务端操作 1.在管理节点创建 mds 服务 2.查看各个节点的 mds 服务&#xff08;可选&#xff09; 3.创建存储池&#xff0c;启用 ceph 文件系统 4.查看mds状态&#xff0c;一个up&#xff0c;其余两个待命&#xff0c;目前的工作的是node01上的mds服务 5.创建用户…

从零开始搭建vue框架

流程图 开始 | |-- 2013 年底&#xff0c;尤雨溪开始开发 Vue 框架&#xff0c;最初命名为 Seed&#xff0c;后更名为 Vue | |-- 2013 年 12 月&#xff0c;Vue 0.6.0 版本 | |-- 2014 年 1 月 24 日&#xff0c;Vue 0.8.0 版本发布 | |-- 2014 年 2 月 25 日&#xff0c;…

数据中心内存RAS技术发展背景

随着数据量的爆炸性增长和云计算的普及&#xff0c;数据中心内存的多比特错误及由无法纠正错误(UE)导致的停机问题日益凸显&#xff0c;这些故障不仅影响服务质量&#xff0c;还会带来高昂的修复或更换成本。随着工作负载、硬件密度以及对高性能要求的增加&#xff0c;数据中心…

408数据结构-图的应用3-有向无环图、拓扑排序 自学知识点整理

前置知识&#xff1a;表达式&#xff0c;图的遍历 有向无环图描述表达式 有向无环图&#xff1a;若一个有向图中不存在环&#xff0c;则称为有向无环图&#xff0c;简称 D A G DAG DAG图 。 &#xff08;图片来自王道考研408数据结构2025&#xff09; 由王道考研-咸鱼学长的讲…

进销存管理系统设计

进销存管理系统&#xff08;Inventory Management System&#xff0c;简称IMS&#xff09;是一种帮助企业有效管理商品的入库、出库及库存情况的信息系统。良好的进销存管理系统能够提升库存周转率、减少库存成本、提高订单处理效率&#xff0c;从而增强企业的市场竞争力。以下…

SD card知识总结

一、基础知识 1、简介 SD Card 全称(Secure Digital Memory Card)&#xff0c;日本电子公司松下&#xff08;Panasonic&#xff09;、瑞典公司爱立信&#xff08;Ericsson&#xff09;、德国公司西门子&#xff08;Siemens&#xff09;共同开发的&#xff0c;于1999年发布根…

超声波清洗机排行榜,热门超声波清洗机哪个更值得入手?

用超声波清洗机洗眼镜已经不算是什么惊奇的事情了&#xff0c;并且很多戴眼镜的朋友更是因为超声波清洗机能够清洗眼镜而慕名前来。毕竟现在洗眼镜能够用超声波清洗机代劳实在是一件很省心的事情&#xff01;~但是&#xff0c;对于超声波清洗机你们真的了解吗&#xff1f;因此本…

从零开始学习cartographer源码 | 番外:如何在wsl内使用clion阅读cartographer源码

从零开始学习cartographer源码 | 番外&#xff1a;如何在wsl内使用clion阅读cartographer源码 安装WSL2及Clion安装WSL2-Ubuntu20.04安装Clion安装ROS 安装Cartographer一键安装Cartographer 在Clion打开cartographer工程安装gdb手动创建CMakeLists.txt打开项目配置wsl工具链配…

一款简单的音频剪辑工具

Hello&#xff0c;大家好呀&#xff0c;我是努力搬砖的小画。 今天小画给大伙分享一款强大的音频剪辑工具--【剪画】&#xff0c;无需下载就能使用&#xff0c;支持对MP3、M4A、AAC等多种格式文件进行剪辑、分割、拼接、混音、变声、淡入淡出、音频格式转换、视频转音频、消除…

网关设备BL122实现Modbus RTU/TCP转Profinet协议

Modbus与Profinet是两种广泛应用于工业自动化领域的通信协议&#xff1a;Modbus因其简单性和兼容性&#xff0c;在许多工业设备中得到广泛应用&#xff1b;而Profinet提供了高速、高精度的通信能力&#xff0c;适合于复杂控制系统和实时应用&#xff0c;但两者之间的差异导致了…