C++二叉搜索树搜索二叉树二叉排序树

news2024/12/23 14:56:52

C++二叉搜索树

1. 二叉搜索树的概念

二叉搜索树BST,Binary Search Tree),也称为二叉排序树或二叉查找树。它与一般二叉树的区别在于:每个结点必须满足“左孩子大于自己,右孩子小于自己”的规则。在这种规则的约束下,二叉搜索树使用中序遍历出来的数据是一个由小到大排列的结果。

在这里插入图片描述

优点:

  1. 查找某个值最多只需要遍历高度次即可,效率高。
  2. 使用中序遍历出来的数据是有序的。

缺点:

  1. 结点的值不允许修改,否则破坏树的结构。

2. 二叉搜索树的插入

二叉搜索树的插入很简单,首先用插入的值从根结点开始比较。插入值小于结点值向左,插入值大于结点值向右,直到结点的左孩子或右孩子为空时结束,为空的位置就是插入的位置。

在这里插入图片描述

4作为插入结点的值,先找到6是插入结点的父结点,再判断4和6的大小关系,4<6,所以插入在6的左边。

在这里插入图片描述

7作为插入结点的值,先找到6是插入结点的父结点,再判断7和6的大小关系,7>6,所以插入在6的右边。

3. 二叉搜索树的删除

二叉搜索的删除需要分两种情况:

3.1 删除的结点是叶子结点

如果删除的结点是叶子结点,将该结点的父结点指针制空,再释放该结点即可。

在这里插入图片描述

3.2 删除的结点不是叶子结点

如果删除的结点不是叶子结点,可以分为三种情况讨论:

3.2.1 左子树为空

如果删除的结点的左子树为空,此时需要判断删除结点与其父子树的关系:我是父结点的左孩子,就让父结点的左指向我的右子树;我是父结点的右孩子,就让父结点的右指向我的右子树

在这里插入图片描述

在这里插入图片描述

3.2.2 右子树为空

右子树为空,原理与上相同,只是父结点改变的是左的指向。

在这里插入图片描述

在这里插入图片描述

3.2.3 左右子树不为空

如果删除结点的左右子树都不为空,那么此时就需要使用替换法的思想。替换法可以使用左子树的最大结点或右子树的最小结点作为替换结点来替换当前结点,再将替换结点删除。所谓“替换”在实际操作中不是把两个结点的值互换,而是将替换结点的值赋给原删除结点,因为替换节点最终要删除,所以不必要对其进行真正的替换操作。

使用最左结点替换:

在这里插入图片描述

使用最右结点替换:

在这里插入图片描述

4.二叉搜索树的退化问题

二叉搜索树的最优情况下,查找效率为logN。但当插入的顺序有序或部分有序时,二叉搜索树的查找效率会下降,极端情况下会退化至N

在这里插入图片描述

按{10,9,8,7,6,5,4}的顺序插入,导致二叉搜索树完全退化。

5. 参考代码

template<class K, class V>
struct BSTreeNode
{
	BSTreeNode<K, V>* _left;
	BSTreeNode<K, V>* _right;
	K _key;
	V _value;

	BSTreeNode(const K& key,const V& value)
		:_left(nullptr),_right(nullptr),_key(key),_value(value)
	{}
};

template<class K, class V>
class BSTree
{
	typedef BSTreeNode<K, V> Node;
public:
	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;
		}
	}
	Node* Find(const K& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key > key)
			{
				cur = cur->_left;
			}
			else if (cur->_key < key)
			{
				cur = cur->_right;
			}
			else
			{
				return cur;
			}
		}
		return nullptr;
	}
	bool Erase(const K& key)
	{
		//先找到该结点
		Node* cur = _root;
		Node* parent = cur;
		while (cur)
		{
			if (cur->_key > key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (cur->_key < key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else
			{
				//如果删除的是叶子结点,直接删除
				if (cur->_left == nullptr && cur->_right == nullptr)
				{
					if (cur == parent->_left)
						parent->_left = nullptr;
					else if (cur == parent->_right)
						parent->_right = nullptr;
					delete cur;
				}
				//如果左为空
				else if (cur->_left == nullptr)
				{
					if (cur == _root)
					{
						_root = cur->_right;
					}
					else
					{
						if (cur == parent->_left)
						{
							parent->_left = cur->_right;
						}
						else if (cur == parent->_right)
						{
							parent->_right = cur->_right;
						}
					}
					delete cur;
				}
				//如果右为空
				else if (cur->_right == nullptr)
				{
					if (cur == _root)
					{
						_root = cur->_left;
					}
					else
					{
						if (cur == parent->_left)
						{
							parent->_left = cur->_left;
						}
						else if (cur == parent->_right)
						{
							parent->_right = cur->_left;
						}
					}
					delete cur;
				}
				// 如果左右都不为空
				else
				{
					//查找右子树的最左结点替换
					Node* RightMinParent = cur;
					Node* RightMin = cur->_right;
					while (RightMin->_left)
					{
						RightMinParent = RightMin;
						RightMin = RightMin->_left;
					}
					cur->_key = RightMin->_key;
					cur->_value = RightMin->_key;
					cur->_value = RightMin->_value;
					if (RightMinParent->_left == RightMin)
					{
						RightMinParent->_left = nullptr;
					}
					else
					{
						RightMinParent->_right = nullptr;
					}
					delete RightMin;
				}
				return true;

			}
		}
		return false;
	}
	void _InOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}
		_InOrder(root->_left);
		cout << root->_key << ":" << root->_value << endl;
		_InOrder(root->_right);
	}
	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}
private:
	Node* _root = nullptr;
};

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

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

相关文章

海外云手机的运作原理和适用场景

海外云手机是一种基于云计算技术的虚拟手机服务&#xff0c;通过将手机操作系统和应用程序托管在远程服务器上&#xff0c;实现用户可以通过互联网连接来使用和管理手机功能&#xff0c;而无需实际拥有物理手机。以下是有关海外云手机的相关信息&#xff1a; 海外云手机的运作原…

【Linux】-Linux的实用操作:快捷键与软件安装操作、构建软连接、日期时区的设置[4]

目录 一、各类小技巧&#xff08;快捷键&#xff09; 1、ctrl c 强制停止 2、ctrl d 退出或登出 3、历史命令搜索 4、光标移动快捷键 5、清屏 二、软件安装 1、yum命令 2、apt命令 - 扩展&#xff08;ubuntu&#xff09; 三、systemctl命令 四、软连接 1、ln命令…

【PG数据库】PostgreSQL 日志归档详细操作流程

1.1 日志归档的目的 pg数据库日志归档是将PostgreSQL数据库的日志文件进行归档的过程。 归档的主要目的是为了保留历史数据&#xff0c;确保数据的一致性和完整性&#xff0c;同时为数据恢复提供必要的支持。 pg数据库日志归档的目的包括&#xff1a; 1.数据恢复&#xff1…

【MySQL】SQL基本知识点DDL(1)

目录 1.SQL分类&#xff1a; 2.DDL-数据库操作 3.DDL-表操作-创建 4.DDL-表操作-查询 5.DDL-表操作-数据类型 6.DDL-表操作-修改 1.SQL分类&#xff1a; 2.DDL-数据库操作 3.DDL-表操作-创建 注意&#xff1a;里面的符号全部要切换为英文状态 4.DDL-表操作-查询 5.DDL…

html基础(全)

html简介 目录 什么是网页 什么是 HTML 常用浏览器 WebE标准的构成 基本语法概述 第一个HTML页面 文档类型声明标签 lang 语言种类 字符集 标题标签 段落和换行标签 文本格式化标签 div和span标签 图像标签和路径 超链接标签 表格的主要作用 表头单元格标签 列…

【汇编语言】多文件组织

【汇编语言】多文件组织 文章目录 【汇编语言】多文件组织前言一、8086拓展1.子程序的另外一种写法2.程序的多文件组织 总结 前言 本篇文章将讲到子程序的另一种写法&#xff0c;以及程序的多文件组织。 一、8086拓展 1.子程序的另外一种写法 初始的程序 在这里我们对比一下…

战网国际服加速器哪个好用 暴雪战网免费加速器分享

战网国际服&#xff08;Battle.net International或Battle.net Global&#xff09;是由暴雪娱乐公司&#xff08;Blizzard Entertainment&#xff09;运营的面向全球玩家的多人在线游戏平台。与专注于特定地区的版本不同&#xff0c;国际服允许玩家不受地域限制地访问暴雪的多款…

一键修复所有dll缺失,教大家解决丢失的dll文件

修复所有DLL&#xff08;动态链接库&#xff09;文件缺失的问题通常不可能通过单一的"一键修复"按钮来实现&#xff0c;因为DLL文件缺失可能由各种不同的原因导致&#xff0c;比如应用程序安装不正确、病毒感染、或系统文件损坏等。 使用内置的系统文件检查器&#x…

AtomicInteger 详细解读

AtomicInteger 详细解读 一、原始数据并发写引发的问题 对于共享变量整数的加减操作&#xff0c;当出现并发的情况时&#xff0c;很容易造成线程不安全。 1、代码示例 public class Demo {static int num 0;public static void main(String[] args) throws InterruptedExc…

【Redis】Redis面试和工作中十有八九会遇到的问题

1. 数据类型 常用的Redis数据类型有5种&#xff0c;分别是&#xff1a; String、List、Set、SortedSet、Hash 还有一些高级数据类型&#xff0c;比如Bitmap、HyperLogLog、GEO等&#xff0c;其底层都是基于上述5种基本数据类型。因此在Redis的源码中&#xff0c;其实只有5种数…

2000-2001年中国净生态系统生产力产品(Net ecosystem productivity, NEP)

简介 中国净生态系统生产力产品(Net ecosystem productivity, NEP)是总初级生产力GPP&#xff08;单位时间内生物通过光合作用所固定的有机碳量&#xff09;扣除自养生物呼吸消耗的部分和异养生物呼吸消耗&#xff08;土壤呼吸&#xff09;光合产物之后的部分&#xff0c;是生…

连锁收银系统如何助力实体门店私域运营

作为实体门店&#xff0c;私域运营是提升客户黏性和增加复购率的重要策略之一。而连锁收银系统在私域运营中扮演了关键的角色&#xff0c;它不仅可以帮助门店管理客户信息和消费记录&#xff0c;还能够通过数据分析和营销功能提供个性化的服务和推广活动。下面看看连锁收银系统…

TypeScript中的泛型(Generics)

TypeScript中的泛型&#xff08;Generics&#xff09; 在前面的几篇文章中&#xff0c;我们了解了TypeScript的类、接口和基本的数据类型系统。本文将重点介绍TypeScript中的泛型&#xff0c;这是一种强大的工具&#xff0c;它允许我们创建可重用的组件&#xff0c;同时保持类…

信创电脑|暴雨新增兆芯KX-7000处理器版本

IT世界 5 月 15 日消息&#xff0c;暴雨公司信创家族新上架了一款搭载兆芯KX-7000系列处理器、摩尔线程8GB 显卡、16G DDR5 内存以及 512G SSD 的新配置台式电脑主机。 兆芯 KX-7000 处理器采用开先的 8 核 Chiplet互联架构&#xff0c;最高频率3.7 GHz&#xff0c;拥有 32MB 的…

11种最常见的网络安全攻击类型!

常见的网络安全攻击类型有很多&#xff0c;其中包括DOS和DDOS攻击、中间人攻击、SQL注入攻击、勒索攻击、网络攻击、蛮力攻击等&#xff0c;接下来这篇文章为大家介绍一下{BANNED}最佳常见的11种网络安全攻击类型&#xff0c;快来看看吧。 1、DOS和DDOS攻击 拒绝服务攻击旨在使…

IDEA 使用Alibaba Cloud Toolkit 实现远程 自动部署

安装插件 maven方式部署 配置服务器主机信息 配置发布到主机 单击Select 单击run 就可以将选择module的jar文件上传到服务器的指定位置了 Alibaba Cloud Toolkit 上传文件的方式部署

网工内推 | 测试工程师,NA认证以上,15薪,补充医疗险

01 天视通 招聘岗位&#xff1a;测试工程师 职责描述&#xff1a;1、网络视频监控相关软件产品测试&#xff0c;及行测试记录和相应各种文档资料/手册编写&#xff1b;2、负责编写测试计划、测试用例、搭建测试环境、执行测试&#xff1b;3、进行BUG验证根据测试结果&#xff…

腾讯云环境安装单机版minio

Minio 下载安装 wget https://dl.min.io/server/minio/release/linux-amd64/minio修改minio 文件为可执行文件 chmod x minio3、启动&#xff0c;随机端口启动 ./minio server /data/miniodata # 或者指定密码执行 MINIO_ACCESS_KEYmyminioadmin MINIO_SECRET_KEYmyminioadm…

UV胶固化时使用的UV灯要如何选择才适合!

近日&#xff0c;在使用UV灯固化 UV胶的过程中&#xff0c;遇到了不同的问题&#xff0c;最基本的就是很多人对于固化UV胶时&#xff0c;使用什么样的UV灯不清楚&#xff0c;从而导致了UV胶在实际使用过程中没有固化&#xff0c;或者没有完全固化&#xff0c;胶水仍处是液体流动…

代码随想录算法训练营第三十一天|455.分发饼干,376. 摆动序列,53. 最大子序和

455.分发饼干 优先把小饼干分给胃口值小的&#xff0c;或者是把大饼干分给胃口大的。 376. 摆动序列 class Solution { public:int wiggleMaxLength(vector<int>& nums) {if (nums.size() < 1) return nums.size();int curDiff 0; // 当前一对差值int preDiff …