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

目录

一、概念:

二、代码实现:

大致结构:

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.mfbz.cn/a/579182.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

springboot+springsecurity+vue前后端分离权限管理系统

有任何问题联系本人QQ: 1205326040 1.介绍 优秀的权限管理系统&#xff0c;核心功能已经实现&#xff0c;采用springbootvue前后端分离开发&#xff0c;springsecurity实现权限控制&#xff0c;实现按钮级的权限管理&#xff0c;非常适合作为基础框架进行项目开发。 2.效果图…

ICP点云配准初探

ICP点云配准初探 1 简介2 常用的点云配准算法3 ICP&#xff08;Iterative Closest Point&#xff0c;最近点迭代法&#xff09;3.1 ICP要解决的问题3.2 ICP的核心思想3.3 算法流程3.4 总结 4 ICP优缺点 1 简介 在逆向工程&#xff0c;计算机视觉&#xff0c;文物数字化等领域中…

香港BTC、ETH现货ETF同时通过,对行业意义几何?

香港比美国更快一步通过以太坊现货 ETF。 2024 年 4 月 15 日&#xff0c;香港嘉实国际资产管理有限公司&#xff08;Harvest Global Investments&#xff09;今天宣布&#xff0c;得到香港证监会的原则上批准&#xff0c;将推出两大数字资产&#xff08;比特币及以太坊&#…

​可视化大屏C位图:园区鸟瞰

将园区鸟瞰图作为可视化大屏设计的焦点图有以下几个好处&#xff1a; 提供全局视图&#xff1a;园区鸟瞰图可以展示整个园区的布局和结构&#xff0c;提供全局视图。这对于大型园区或复杂的场所来说尤为重要&#xff0c;用户可以一目了然地了解整个园区的规模、分布和关联关系…

go设计模式之工厂方法模式

工厂方法模式 什么是工厂方法模式 工厂方法模式是一种创建型设计模式&#xff0c;它定义了一个用于创建对象的接口&#xff0c;让子类决定实例化哪一个类。工厂方法使一个类的实例化推迟到其子类。 这个接口就是工厂接口&#xff0c;子类就是具体工厂类&#xff0c;而需要创…

频率分析和离散傅里叶变换——DSP学习笔记四

背景知识 四种基本的傅里叶变换 基本思想&#xff1a;将信号表示为不同频率 正弦分量的线性组合 正弦信号和复指数时间信号的有用特性 相同频率但不同相位的正弦信号的任何线性组合&#xff0c;都是有着相同频率但不同相位&#xff0c;且幅度可能受改变的正弦信号。 复指数时…

EXCEL表格中的数字,为什么每次打开会自动变成日期?

一、典型现象 在工作中&#xff0c;有时会发现公司里的报表&#xff0c;经过多人多次的重复的使用和修改后&#xff0c;会出现这种情况&#xff1a; 1.在表格里按照需要输入数字&#xff0c;保存工作簿。 2.然而&#xff0c;再次打开工作簿&#xff0c;里面的数字变成日期&a…

Linux多线程(二) 线程同步 信号量互斥锁读写锁条件变量

多个进程同时访问某些资源时&#xff0c;必须考虑同步问题&#xff0c;以确保任一时刻只有一个进程可以拥有对资源的独占式访问。通常&#xff0c;程序对关键资源的访问代码只是很短的一段&#xff0c;我们称这段代码为关键代码段或者临界区&#xff0c;对进程同步&#xff0c;…

火绒安全概述

页面简介&#xff1a; 火绒安全是一款集多种安全功能于一体的国产软件&#xff0c;旨在为用户提供全面的计算机保护。本页面将详细介绍火绒安全的核心功能和使用方式。 页面内容概览&#xff1a; 杀毒防护 实时监控&#xff1a;详细介绍火绒安全如何实时检测系统中的文件和程序…

【强训笔记】day5

NO.1 思路&#xff1a;找到数量最小的字符&#xff0c;就可以知道you的数量&#xff0c;用o的数量减去you的数量再减去1就是oo的数量。 代码实现&#xff1a; #include<iostream>using namespace std;int main() {int q;cin >> q;int a, b, c;while (q--){cin &g…

Java web应用性能分析之【sysbench基准测试】

Java web应用性能分析之【CPU飙高分析之MySQL】-CSDN博客 Java web应用性能分析之【Linux服务器性能监控分析概叙】-CSDN博客 Java web应用性能分析概叙-CSDN博客 Java web应用性能分析之【基准测试】-CSDN博客 上面基本科普了一下基准测试&#xff0c;这里我们将从sysbench…

雷电模拟器,安卓手机模拟器电脑端去广告精简优化版 v9.0.70 (240427)

软件介绍 在众多安卓模拟器中&#xff0c;雷电模拟器作为电脑端手游的首选平台&#xff0c;由上海畅指网络科技有限公司研发并免费提供给用户。此模拟器搭载了先进的内核技术&#xff08;基于版本&#xff09;&#xff0c;确保了软件运行的高速性和稳定性。雷电模拟器还引入了…

【yolov8yolov5驾驶员抽烟-打电话-喝水-吃东西检测】

YOLO算法DMS驾驶员抽烟-打电话-喝水-吃东西检测数据集 YOLOv8和YOLOv5是深度学习中用于目标检测的先进算法&#xff0c;它们在实时性和准确性方面表现出色&#xff0c;适用于各种视频监控和图像处理应用&#xff0c;包括驾驶员行为监测。这些算法通过单次前向传播即可预测图像…

javaScript基础2

javaScript 一.运算符二.流程控制1.顺序流程控制2.分支流程控制&#xff08;1&#xff09;if/if..else/if多分支&#xff08;2&#xff09;.三元表达式&#xff08;4&#xff09;.switch和if else区别 3.循环流程控制(1).for循环/双重for循环(2).一些例子(3).while循环/do..whi…

SpringBoot 接口防抖(防重复提交)的一些实现方案

啥是防抖 所谓防抖&#xff0c;一是防用户手抖&#xff0c;二是防网络抖动。 在Web系统中&#xff0c;表单提交是一个非常常见的功能&#xff0c;如果不加控制&#xff0c;容易因为用户的误操作或网络延迟导致同一请求被发送多次&#xff0c;进而生成重复的数据记录。 要针对…

【C++ | 复合类型】结构体、共用体、枚举、引用

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; ⏰发布时间⏰&#xff1a; 本文未经允许…

深入理解冯诺依曼体系结构

文章目录 冯诺依曼体系结构概念冯诺依曼体系结构的优势冯诺依曼体系结构的现实体现 冯诺依曼体系结构概念 冯诺依曼体系结构也称普林斯顿结构&#xff0c;是现代计算机发展的基础。它的主要特点是“程序存储&#xff0c;共享数据&#xff0c;顺序执行”&#xff0c;即程序指令和…

芋道微服务功能介绍(限免)

&#x1f339;作者主页&#xff1a;青花锁 &#x1f339;简介&#xff1a;Java领域优质创作者&#x1f3c6;、Java微服务架构公号作者&#x1f604; &#x1f339;简历模板、学习资料、面试题库、技术互助 &#x1f339;文末获取联系方式 &#x1f4dd; 系列文章目录 第一章 芋…

Datart 扩装下载功能之PDF和图片下载

Datart 扩装下载功能之PDF和图片下载 首先下载依赖 yum install mesa-libOSMesa-devel gnu-free-sans-fonts wqy-zenhei-fonts -y 然后下载安装chrome yum install https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm 查看chrome版本号 google…

vscode使用EditorConfig进行项目配置

安装 EditorConfig for VS Code 插件&#xff0c;该插件会自动读取项目的 .editorconfig 文件&#xff0c;对项目进行配置。 该文件支持属性&#xff1a; indent_style&#xff1a;缩进风格&#xff0c;可配置项&#xff1a;tab&#xff0c;spaceindent_size&#xff1a;缩进…
最新文章