【C++】简易二叉搜索树

news2024/11/20 3:18:54

目录

一、概念:

二、代码实现:

大致结构:

1、遍历:

2、insert

3、find

4、erase

三、总结:


一、概念:

        二叉搜索树又称为二叉排序树,是一种具有特殊性质的二叉树,对于每一个节点而言,节点的左子树要比节点的值小,节点的右子树要比节点的值大,也就是说节点的左右子树也是二叉搜索树。

例如如下二叉树:

拿根节点8来讲,8的左子树都是比8小的值,而8的右子树都是比8大的值,与此同时,8的左右子树也是二叉搜索树,拿8的左子树的根节点3来讲,3的左子树都是比3大的值,3的右子树都是比3小的值。以此反复。

二、代码实现:

大致结构:

// 二叉树的节点: 
template<class K>
struct BSTreeNode
{
	BSTreeNode<K>* left;
	BSTreeNode<K>* right;
	K _key;

	// 节点的构造函数
	BSTreeNode(const K& key)
		:left(nullptr)
		,right(nullptr)
		,_key(key)
	{}
};

// 搜索二叉树的功能:
template<class K>
class BSTree
{
	typedef BSTreeNode<K> Node;

public:
    // 插入:
	bool insert(const K& key);

    // 查找:
	bool Find(const K& key);

    // 删除:
	bool erase(const K& key)

    // 遍历: 
	void InOrder();

private:
	Node* _root = nullptr; // 根节点
};

1、遍历:

        前面说过,在搜索二叉树中对于每一个节点而言,比它大的值在右边,比它小的值在左边,这种特性如果用二叉树的中序遍历打印出来的就是一个有序的数列,所以搜索二叉树的遍历可以直接写中序遍历,但要注意的是,在上面的大致结构中二叉搜索树的类中的根节点是私有的,而中序遍历又需要用到根节点,所以我们可以直接嵌套一层:

template<class K>
class BSTree
{
	typedef BSTreeNode<K> Node;

public:

    // 其他成员函数

	void InOrder()
	{
		_InOrder(_root);
	}
private:
	void _InOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}
		_InOrder(root->left);
		cout << root->_key << " ";
		_InOrder(root->right);
	}


private:
	Node* _root = nullptr;
};

2、insert

        值插入需要从根节点开始往下遍历到空位置(比节点大的往右走,比节点小的往左走),需要注意的是,期间如果遇到相同的值(也就是重复的情况)就可以不用进行插入了,因为搜索树不支持冗余。

        思路:如果树为空,那么直接定义一个节点即可,不为空则设置一个指针cur指向根节点并往下遍历,注意cur注定会走到空,所以要多设置一个指针parent代表cur的父节点。最后如果插入值比父节点大则是parent的右孩子,否则就是左孩子。

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 // 否则就是左节点
	{
		parent->left = cur;
	}

	return true;
}

代码测试:

// 往搜索树中插入一段数据,然后遍历出来(搜索树的中序遍历出来是有序的数列)
int main()
{
	int a[] = { 8,3,1,10,6,4,7,14,13 };
	BSTree<int> t1;

	for (int i : a)
	{
		t1.insert(i);
	}

	t1.InOrder();
}

3、find

        值查找就是从根节点向下遍历,比节点值大的往右走,比节点值小的往左走,找到返回true找不到返回false。

bool Find(const K& key)
{
	Node* cur = _root;
	while (cur)
	{
		if (cur->_key < key)
		{
			cur = cur->right;
		}
		else if (cur->_key > key)
		{
			cur = cur->left;
		}
		else
		{
			return true;
		}
	}
	return false;
}

4、erase

搜索二叉树的删除值操作分为好几种情况,以下一一列举:

情况一:无左右孩子,例如节点 4、7、13,这种就可以直接删除。(这里有一种特殊情况需要特殊处理,那就是整棵树只有这一个节点,需要把该节点置空,不能直接删)

情况二:只有左孩子或者只右孩子,例如节点 10、14,这种需要记录其父节点,然后让其父节点指向自己的孩子节点,再删除该节点。

情况三:节点的左右孩子同时存在,例如节点8,3,6,此时需要用到替换法(以节点8为例),用该节点的左子树最大的值(7)或者右子树最小的值(10)替换该节点,然后再删除被替换的节点即可(此时还需注意被替换的点还有没有孩子,有的话删除前还需要将被替换节点的孩子交给被替换节点的父节点保管)。(也就是左子树找最右的叶子节点或者右子树找最左的叶子节点)

代码实现:

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 // 找到该值,进行条件划分并删除
		{
			if (cur->left == nullptr) // 左为空,父节点指向我的右孩子
			{
				if (cur == _root) _root = cur->right; // 处理单枝或单节点情况
				else
				{
					if (cur == parent->left) // 判断被删除的节点是父节点的左还是右,用于子节点链接
					{
						parent->left = cur->right;
					}
					else
					{
						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
					{
						parent->right = cur->left;
					}
				}
				delete cur;
			}
			else // 左右孩子都不为空,使用替换法删除节点
			{
				// 这里使用左子树的最右节点替代
				Node* leftMaxParent = cur;
				Node* leftMax = cur->left;

				while (leftMax->right)
				{
					leftMaxParent = leftMax;
					leftMax = leftMax->right;
				}

				swap(cur->_key, leftMax->_key);

				// 更改子节点指向
				if (leftMax == leftMaxParent->left) leftMaxParent->left = leftMax->left;
				else leftMaxParent->right = leftMax->left;


				delete leftMax;
			}
			return true;
		}
	}
	return false;
}

三、总结:

二叉搜索树的应用场景:

K模型:K模型即只有Key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到的值。

例如:

        构建一棵二叉搜索树,将词库中的每个单词作为Key,然后输入一个单词word,判断该单词是否拼写正确。就是在树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。

KV模型:每一个关键码Key,都有与之对应的值Value,即<Key,Value>的键值对。

例如英汉字典就是一种英文与中文的对应关系,英文单词与其对应的中文<word,chinese>就构成一种键值对。将其存入树中,输入英文就可以返回对应中文。     

二叉搜索树的性能效率:

        在对二叉搜索树进行insert或是erase时都先必须进行find操作,因此find的效率也就代表了各个操作的性能。但是这也取决于树的形状。

最优情况下:二叉搜索树为完全二叉树(或者接近完全二叉树),效率为O(logn)

最差情况下:二叉搜索树退化为单支树(或者类似单支),效率为O(n)

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

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

相关文章

icloud里面的通讯录怎么全部导出,通讯录格式如何转换,简单!

随着科技的发展&#xff0c;我们的日常生活越来越离不开手机和各种应用程序。通讯录作为手机中最重要的功能之一&#xff0c;记录着我们的亲朋好友、同事和业务伙伴的联系方式。因此&#xff0c;定期备份通讯录变得尤为重要。iCloud作为苹果公司提供的一项云服务&#xff0c;可…

基于jenkins+docker实现CI/CD实践

项目简介 利用 Jenkins、Docker、SonarQube 和 Harbor 技术&#xff0c;搭建一个完整的 CI/CD 管道&#xff0c;实现持续集成、持续交付和持续部署的流程。通过自动化构建、测试、代码质量检查和容器化部署&#xff0c;将开发人员从繁琐的手动操作中解放出来&#xff0c;提高团…

求三个字符数组最大者(C语言)

一、N-S流程图&#xff1b; 二、运行结果&#xff1b; 三、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h> # include <string.h>int main() {//初始化变量值&#xff1b;int i 0;char str[3][20];char string[20];//循环输入3个字符…

可以在手机端运行的大模型标杆:微软发布第三代Phi-3系列模型,评测结果超过同等参数规模水平,包含三个版本,最小38亿,最高140亿参数

本文原文来自DataLearnerAI官方网站&#xff1a; 可以在手机端运行的大模型标杆&#xff1a;微软发布第三代Phi-3系列模型&#xff0c;评测结果超过同等参数规模水平&#xff0c;包含三个版本&#xff0c;最小38亿&#xff0c;最高140亿参数 | 数据学习者官方网站(Datalearner…

React真的好难用

我发现React就像个宗教一样&#xff0c;网络上总有一群信徒。信徒&#xff1a;React天下第一&#xff0c;谁也不能说他不好。 网络上大佬对React的评价一般有几类&#xff1a; React跟Vue比就是手动档和自动档的区别&#xff0c;高手都开手动档。—— 就一个破打工的&#xf…

(待更)DRF: 序列化器、View、APIView、GenericAPIView、Mixin、ViewSet、ModelViewSet的源码解析

前言&#xff1a;还没有整理&#xff0c;后续有时间再整理&#xff0c;目前只是个人思路&#xff0c;文章较乱。 注意路径匹配的“/” 我们的url里面加了“/”&#xff0c;但是用apifox等非浏览器的工具发起请求时没有加“/”&#xff0c;而且还不是get请求&#xff0c;那么这…

【知识】pycolmap.Sift.extract的参数和返回格式

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;请不吝给个[点赞、收藏、关注]哦~ import pycolmap# 比较推荐的参数 options {"peak_threshold": 0.0066667,"edge_threshold": 10,"first_octave"…

基于Spring Boot的火车订票管理系统设计与实现

基于Spring Boot的火车订票管理系统设计与实现 开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/idea 系统部分展示 前台首页功能界面图&#xff0c;在系统首页可以查看…

python 函数作业 计算三角形的有效值s

题目&#xff1a; 计算半周长p&#xff0c;既p(abc)/2。 将p的值代入公式&#xff1a;计算出三角形的有效值。&#xff08;只能用python函数的方法求解&#xff09; 第一步先定义个函数&#xff1a; def isValid(side1,side2,side3) 第二步用if判断是否符合三角形边长准…

芯片尺寸封装(CSP)/晶圆级封装(WLP)/芯片尺寸晶圆级封装(CSWLP)

芯片尺寸封装&#xff08;CSP&#xff09;、晶圆级封装&#xff08;WLP&#xff09;、晶圆级芯片尺寸封装&#xff08;WLCSP&#xff09; 1.芯片尺寸封装&#xff08;CSP&#xff09;的定义是其尺寸不超过裸片尺寸的1.1倍。 2.晶圆级封装&#xff08;WLP&#xff09;是在晶圆…

AIGC——什么是人工智能生成内容

人工智能生成内容&#xff08;AIGC&#xff09;是当今数字时代的一个引人注目的前沿技术&#xff0c;它借助深度学习和自然语言处理等技术&#xff0c;使计算机系统具备了生成高质量文本、图像、音频等多媒体内容的能力。AIGC的出现不仅推动了信息技术的发展&#xff0c;也在多…

Linux搭建本地DNS服务器

目录 DNS进行域名解析的过程&#xff1a; 环境介绍&#xff1a; 环境准备&#xff1a; 1.安装bind 2.编辑主配 3.配置正向解析文件 4.测试&#xff1a; DNS进行域名解析的过程&#xff1a; 用户要访问www.baidu.com&#xff0c;会先找本机的host文件&#xff0c;再找本…

selenium 自动化测试课上实操指南2——乐视tv搜索

如果完成了实操1的同学&#xff0c;环境搭建已经ok&#xff0c;环境还没有好的同学请参考 实操1_百度搜索 为了大家顺利&#xff0c;我们还想按照实操1那样&#xff0c;先导入一个基本项目。在次基础上进行代码编写、 我们一起写写看。 1.打开乐视视频网页并最大化 如下图所…

深度学习从入门到精通——词向量介绍及应用

词向量介绍 词向量&#xff08;Word embedding&#xff09;&#xff0c;即把词语表示成实数向量。“好”的词向量能体现词语直接的相近关系。词向量已经被证明可以提高NLP任务的性能&#xff0c;例如语法分析和情感分析。词向量与词嵌入技术的提出是为了解决onehot的缺陷。它把…

【Yolov系列】Yolov5学习(一):大致框架

一、Yolov5网络结构 Yolov5特点&#xff1a; 合适于移动端部署&#xff0c;模型小&#xff0c;速度快 Yolov5骨干结构&#xff1a;CSPDarknet53网络Yolov5主要有Yolov5s、Yolov5m、Yolov5l、Yolov5x四个版本。这几个模型的结构基本一样&#xff0c;不同的是depth_multiple模型…

AUTOSAR-SD篇

1 概述 服务发现模块的主要任务是管理在车内通信中被称为服务的功能实体的可用性&#xff0c;以及控制事件消息的发送行为。只允许向需要这些事件消息的接收器发送事件消息&#xff08;发布/订阅&#xff09;。 这里描述的解决方案也被称为SOME/IP-SD&#xff08;基于IP -服务发…

西湖大学赵世钰老师【强化学习的数学原理】学习笔记-1、0节

强化学习的数学原理是由西湖大学赵世钰老师带来的关于RL理论方面的详细课程&#xff0c;本课程深入浅出地介绍了RL的基础原理&#xff0c;前置技能只需要基础的编程能力、概率论以及一部分的高等数学&#xff0c;你听完之后会在大脑里面清晰的勾勒出RL公式推导链条中的每一个部…

索引失效的几种场景

索引失效的几种场景 初始化数据一、对索引使用左或左右模糊匹配二、对索引使用函数三、对索引使用表达式计算四、对索引进行隐式类型转换五、索引使用不满足最左前缀原则六、where子句使用or总结 初始化数据 本文使用的是InnoDB存储引擎&#xff0c;先来创建一个学生表。 dro…

2021年山东省职业院校技能大赛高职组“信息安全管理与评估”样题

培训、环境、资料、考证 公众号&#xff1a;Geek极安云科 网络安全群&#xff1a;624032112 网络系统管理群&#xff1a;223627079 网络建设与运维群&#xff1a;870959784 移动应用开发群&#xff1a;548238632 极安云科专注于技能提升&#xff0c;赋能 2024年广东省高校的技…

【第二十五课】动态规划:数字三角形(acwing-898 / 蓝桥官网503 / c++代码)

目录 acwing-898数字三角形(模板题) 思路 注意点 代码 视频讲解推荐 2020蓝桥杯省赛-数字三角形 错误思路 (可不看) 思路 代码 注意点 续上之前的啦。 【第二十五课】动态规划&#xff1a;01背包问题(acwing-2 / 思路 / 含一维数组优化 / c代码) 适合在学习过背包…