【C++】二叉搜索树的底层以及实现

news2024/11/14 0:53:27

在这里插入图片描述
个人主页
在这里插入图片描述

文章目录

  • ⭐一、二叉搜索树的概念
  • 🚀二、二叉搜索树性能分析
  • 🏝️三、二叉搜索树的操作
    • 1. 插入
    • 2. 查找
    • 3. 删除
    • 4. 遍历节点
  • 🎄四、二叉搜索树的实现(K模型)
  • 🎉五、二叉搜索树的应用
    • 1. K模型
    • 2. KV模型

⭐一、二叉搜索树的概念

二叉搜索树(Binary Search Tree,简称BST),又称为二叉查找树或二叉排序树,是一种特殊的二叉树结构。其具有以下几个性质:
左子树: 若它的左子树不为空,则左子树上所有结点的值都小于等于根结点的值。
右子树: 若它的右子树不为空,则右子树上所有结点的值都大于等于根结点的值。
递归: 它的左右子树也分别为二叉搜索树。

注: 二叉搜索树可以支持插入相等的值,也可以不支持插入相等的值,具体看使用的场景来定义。
在这里插入图片描述

🚀二、二叉搜索树性能分析

最优情况下(即树是平衡的),二叉搜索树为完全二叉树(或者接近完全二叉树),其高度为: O(log2 N)
最差情况下(即树退化成为一个链表),二叉搜索树退化为单支树(或者类似单支),其高度为: O( N / 2)

综合而言二叉搜索树增删查改时间复杂度为: O(N)
但这样的效率显然是不满足我们需求的,我们后面学到的AVL树和红黑树,才能适用于我们内存中存储和搜索数据。

注:虽然二分查找也能实现O(logN) 级别的查找效率,但它有两大缺陷
1.需要存储在支持下标随机访问的结构中,并且有序
2.插入和删除数据效率很,因为存储在下标随机访问的结构中,插入和删除数据一般需要挪动数据

这也就体现出了平衡二叉搜索树的价值。
在这里插入图片描述

🏝️三、二叉搜索树的操作

1. 插入

插入节点操作的具体步骤如下:
1.树为空,则直接新增结点,赋值给root指针
2.树不空,按二叉搜索树性质,插入的值比当前结点大时往右走,插入值比当前结点小时往左左,直到找到空位置时,插入新结点。
3.如果支持插入相等的值,插入值跟当前结点相等的值可以往右走,也可以往左走,直到找到空位置,插人新结点。(要注意的是要保持逻辑一致性,插入相等的值不要一会往右走,一会往左走
在这里插入图片描述

下面是插入节点操作的代码实现:

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* cur = _root;
		Node* parent = nullptr;
		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;
	}

2. 查找

查找节点操作的具体步骤如下:
1.从根开始进行比较,查找x,x比根的值大则往右边进行查找,x比根的值小则往左边进行查找。
2.最多查找高度次,如果走到空时还没找到,则说明这个值不存在。
3.如果不支持插入相等的值,则找到x即可返回
4.如果支持插入相等的值,意味着有多个x存在,一般要求查找中序的第⼀个x。如下图,查找3,要找到1的右孩子的那个3进行返回
在这里插入图片描述

查找节点操作具体代码如下:

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;
}

3. 删除

删除节点的操作就是在树中移除一个指定的节点。但删除操作相对复杂一点,因为我们要考虑删除过后如何保持树的有序性。

删除节点操作具有以下四种情况:
1.要删除结点N左右孩子均为空
2.要删除的结点N左孩子位空,右孩子结点不为空
3.要删除的结点N右孩子位空,左孩子结点不为空
4.要删除的结点N左右孩子结点均不为空

那么如何解决它们所对应的情况呢?其对应的方案:
1.把N结点的父亲对应的孩子指针指向空,直接删除N结点
2.把N结点的父亲对应的孩子指针指向N的右孩子,直接删除N结点
3. 把N结点的父亲对应的孩子指针指向N的左孩子,直接删除N结点
4. 因为我们无法直接删除N结点,所以只能用替换法进行删除。找N左子树的值最大结点R(最右结点)或者N右子树的值最小结点R(最左结点)来替代N,因为这两个结点中任意⼀个放到N的位置,都满足二叉搜索树的规则。
(注:替代N的意思就是将N和R的两个结点的值交换,转变成删除R的结点)

删除节点操作具体代码如下:

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 (parent->_left == cur)
					{
						parent->_left = cur->_right;
					}
					else
					{
						parent->_right = cur->_right;
					}
				}
				delete cur;
			}
			else if (cur->_right == nullptr)
			{
				//右为空
				if (cur == _root)
				{
					_root = cur->_left;
				}
				else
				{
					if (parent->_right == cur)
					{
						parent->_right = cur->_left;	
					}
					else
					{
						parent->_left = cur->_left;
					}
				}
				delete cur;
			}
			else
			{
				//左右都不为空
				//右子树最左节点
				Node* replaceparent = cur;
				Node* replace = cur->_right;
				while (replace->_left)
				{
					replaceparent = replace;
					replace = replace->_left;
				}
				cur->_key = replace->_key;
				if (replaceparent->_left == replace)
				{
					replaceparent->_left = replace->_right;
				}
				else
				{
					replaceparent->_right = replace->_right;
				}
				delete replace;
			}
			return true;
		}
	}
	return false;
}

4. 遍历节点

遍历节点是按照一定的顺序去访问树中的所有节点,常用的遍历方式有:前序遍历、中序遍历和后序遍历。在这里我们采用中序遍历的方法,因为它可以按小到大的顺序输出树中所有的值。

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

🎄四、二叉搜索树的实现(K模型)

下面是二叉树实现的源代码:

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* cur = _root;
		Node* parent = nullptr;
		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;
	}

	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;
	}

	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 (parent->_left == cur)
						{
							parent->_left = cur->_right;
						}
						else
						{
							parent->_right = cur->_right;
						}
					}
					delete cur;
				}
				else if (cur->_right == nullptr)
				{
					//右为空
					if (cur == _root)
					{
						_root = cur->_left;
					}
					else
					{
						if (parent->_right == cur)
						{
							parent->_right = cur->_left;
						}
						else
						{
							parent->_left = cur->_left;
						}
					}
					delete cur;
				}
				else
				{
					//左右都不为空
					//右子树最左节点
					Node* replaceparent = cur;
					Node* replace = cur->_right;
					while (replace->_left)
					{
						replaceparent = replace;
						replace = replace->_left;
					}
					cur->_key = replace->_key;
					if (replaceparent->_left == replace)
					{
						replaceparent->_left = replace->_right;
					}
					else
					{
						replaceparent->_right = replace->_right;
					}
					delete replace;
				}
				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;
};

🎉五、二叉搜索树的应用

1. K模型

在K模型中,搜索二叉树仅存储关键码(key),不存储与关键码相关联的值(Value),关键码即为需要搜索或存储的值。
K模型代码可以参考搜索二叉树的实现。

特点:存储简单(每个节点只存储了一个键值,减少了空间);查找效率高。

2. KV模型

二叉搜索树的KV模型是一种特殊的数据结构。在KV模型中,每个节点不仅存储一个键(Key),还存储一个与该键相关联的值(Value)。这种模型在处理需要快速通过键来检索值的场景时非常有用,如简单的中英互译、统计一篇文章中单词出现的次数等。

KV模型的搜索场景实现的二叉树搜索树支持修改,但是不支持修改key,因为修改key破坏搜索树结构了,可以修改value。

下面是KV模型的二叉搜索树的代码实现:

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

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

template<class K,class V>
class BSTree
{
	using Node = BSTNode<K,V>;
public:
	bool Insert(const K& key,const V& value)
	{
		if (_root == nullptr)
		{
			_root = new Node(key,value);
			return true;
		}
		Node* cur = _root;
		Node* parent = nullptr;
		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->_right;
			}
			else if (cur->_key > key)
			{
				cur = cur->_left;
			}
			else
			{
				return cur;
			}
		}
		return nullptr;
	}

	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 (parent->_left == cur)
						{
							parent->_left = cur->_right;
						}
						else
						{
							parent->_right = cur->_right;
						}
					}
					delete cur;
				}
				else if (cur->_right == nullptr)
				{
					//右为空
					if (cur == _root)
					{
						_root = cur->_left;
					}
					else
					{
						if (parent->_left == cur)
						{
							parent->_left = cur->_left;
						}
						else
						{
							parent->_right = cur->_left;
						}
					}
					delete cur;
				}
				else
				{
					//左右都不为空
					//右子树最左节点
					Node* replaceparent = cur;
					Node* replace = cur->_right;
					while (replace->_left)
					{
						replaceparent = replace;
						replace = replace->_left;
					}
					cur->_key = replace->_key;
					if (replaceparent->_left == replace)
					{
						replaceparent->_left = replace->_right;
					}
					else
					{
						replaceparent->_right = replace->_right;
					}
					delete replace;
				}
				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 << ":" << root->_value << endl;
		_Inorder(root->_right);
	}
private:
	Node* _root = nullptr;
};

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

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

相关文章

14. PEFT:在大模型中快速应用 LoRA

如果你对LoRA还没有一个直观的概念&#xff0c;可以回看这篇文章&#xff1a;《3. 认识 LoRA&#xff1a;从线性层到注意力机制》。 我们将在这里进一步探讨如何快速地在大型预训练模型中应用 LoRA&#xff0c;并解答可能存在的问题&#xff0c;包括&#xff1a; peft 和 lora …

NSSCTF刷题篇1

js类型 [SWPUCTF 2022 新生赛]js_sign 这是一道js信息泄露的题目直接查看源码&#xff0c;有一个main.js文件点击之后&#xff0c;有一串数字和一段base64编码&#xff0c;解开base64编码得到这个编码为敲击码 解码在线网站&#xff1a;Tap Code - 许愿星 (wishingstarmoye.…

828华为云征文|华为云Flexus云服务器X实例之openEuler系统下部署k8s管理面板KubePi

828华为云征文&#xff5c;华为云Flexus云服务器X实例之openEuler系统下部署k8s管理面板kubepi 前言一、Flexus云服务器X实例介绍1.1 Flexus云服务器X实例简介1.2 Flexus云服务器X实例特点1.3 Flexus云服务器X实例使用场景 二、 KubePi介绍2.1 KubePi简介2.2 KubePi主要特点&am…

序列化方式二——JSON之Gson

Gson 1、什么是Gson? Gson是Google提供的一个用于Java编程语言的JSON&#xff08;JavaScript Object Notation&#xff09;序列化和反序列化库。它允许开发者在Java对象和JSON数据之间进行高效的映射和转换。 官网地址&#xff1a;https://github.com/google/gson 官网文档…

小程序隐私合规自查指南

一 背景&#xff1a;小程序作为一种轻量级应用&#xff0c;广泛应用于各大互联网平台。工信部通报2022年第5批侵害用户权益名单中首次出现8款违规小程序。各监管单位对“小程序”违规收集个人信息监控手段和监控力度不断加强。 工信部APP违法违规通报 上海市委网信办查处违规小…

Python_控制循环语句

if语句单分支结构的语法形式如下&#xff1a; 【操作】输入一个数字&#xff0c;小于10&#xff0c;则打印这个数字(if_test01.py)&#xff1a; num input("输入一个数字&#xff1a;") if int(num)<10: print("小于10的数&#xff1a;"num)条件表达式…

BOE(京东方)携多场景物联网创新应用亮相2024服贸会 “屏之物联”赋能数字经济

9 月 12 日&#xff0c;以“全球服务 互惠共享”为主题的2024中国国际服务贸易交易会&#xff08;以下简称“服贸会”&#xff09;在北京拉开帷幕。作为领先的物联网创新企业&#xff0c;BOE&#xff08;京东方&#xff09;携智慧办公、智慧商显、智能车载、智慧教育、智能工厂…

设计模式例题

答案&#xff1a;D C A D 知识点&#xff1a; 观察者模式的意图&#xff1a;定义对象间的一种一对多的依赖关系&#xff0c;当一个对象的状态发生改变时&#xff0c;所有依赖它的对象都得到通知并被自动更新&#xff0c;和自媒体很相似&#xff0c;自媒体更新内容&#xff0c…

C++--C++11(下)

目录 7.5 完美转发 8 新的类功能 9 可变参数模板 10 lambda表达式 11 包装器 7.5 完美转发 模板中的 && 万能引用 void Fun(int &x){ cout << "左值引用" << endl; } void Fun(const int &x){ cout << "const 左值引用…

秒变 Vim 高手:必学的编辑技巧与隐藏功能大揭秘

文章目录 前言一、vi与vim二、Vim的三种模式1. 普通模式2. 插入模式3. 命令模式 三、Vim中的查找与替换1. 查找2. 替换 四、给Vim设置行号1. 临时显示行号2. 永久显示行号 总结 前言 在Linux系统中&#xff0c;文本编辑器是开发者和系统管理员日常工作中的重要工具之一。其中&…

DeepSeek 2.5本地部署的实战教程

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于大模型算法的研究与应用。曾担任百度千帆大模型比赛、BPAA算法大赛评委,编写微软OpenAI考试认证指导手册。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。授权多项发明专利。对机器学…

想学习下Python和深度学习,Python需要学习到什么程度呢?

想要学习Python和深度学习&#xff0c;Python的学习程度需要达到能够熟练运用这门语言进行编程&#xff0c;并能够理解和实现深度学习模型的基本构建和训练过程。以下是一些推荐的书籍&#xff0c;可以帮助你系统地学习Python和深度学习&#xff1a; Python学习推荐书籍 《Py…

kubectl 执行一条命令之后发生了什么?

kubectl 是与 Kubernetes 集群交互的命令行工具&#xff0c;用户通过它可以对集群资源进行操作和管理。你有没有想过&#xff0c;当我们执行一条 kubectl 命令之后&#xff0c;背后都发生了什么&#xff1f; 详细过程 kubectl -> kube-api-server 根据通信类型&#xff0…

【大模型】AutoDL部署AI绘图大模型Stable Diffusion使用详解

目录 一、前言 二、AI绘图大模型概述 2.1 AI绘图大模型介绍 2.2 AI绘图大模型特点 2.3 AI绘图大模型优势 三、主流的AI绘图大模型介绍 3.1 Midjourney 3.1.1 Midjourney介绍 3.1.2 Midjourney功能特点 3.1.3 Midjourney使用场景 3.2 Stable Diffusion 3.2.1 Stable …

zynq的PS端mac与RTL8211F的连接要点

目录 1 VCCO_MIO12 PS_MIO_VREF3 PS的引脚4 RXDLY TXDLY5 ZYNQ的MAC可以调整延时吗 1 VCCO_MIO1 接1.8V 2 PS_MIO_VREF 接0.9V&#xff0c;可通过电阻分压 可通过电阻分压 3 PS的引脚 4 RXDLY TXDLY RXDLY RXD[0] TXDLY RXD[1] 与XC7Z020的PS端MAC连接&#xff0c;必须…

python画正方形、平行四边形、六边形、五角星、风车(四个半圆)

画正方形、平行四边形、六边形、五角星、风车&#xff08;四个半圆&#xff09; import turtle def square(side_length):"""正方形"""for _ in range(4):turtle.forward(side_length)turtle.right(90)def parallelogram(base, height):"&q…

C++——模拟实现string

1.再谈string string为什么要被设计成模板&#xff1f;日常使用string好像都是char*&#xff0c;char*不够使用吗&#xff0c;为什么要设计成模板呢&#xff1f; 1.1 关于编码 //计算机的存储如何区分呢&#xff1f;int main() {//比如在C语言中&#xff0c;有整型//如果是有…

Linux网络——HTTP协议详解(2)

文章目录 HTTP方法GET方法POST方法 状态码与报头状态码报头 会话 HTTP方法 HTTP方法有这些 但是怎么说呢&#xff0c;这些方法只有GET和POST方法是99%情况用到的 剩下的几乎不太用&#xff0c;如果有兴趣可以找《图解HTTP》&#xff0c;是处于了解的范畴 大家肯定一看就明白…

Qt Creator项目模板介绍

在Qt Creator中创建项目时&#xff0c;用户可以从多个模板类别中进行选择&#xff0c;以满足不同的开发需求。 Application(Qt) 在Application(Qt)类别下&#xff0c;Qt Creator提供了多种用于创建不同类型Qt应用程序的模板。这些模板主要包括&#xff1a; Qt Widgets Applic…

专业解析:移动硬盘“要求格式化”背后的真相与数据救援策略

引言&#xff1a;格式化预警下的数据危机 在日常的数字生活中&#xff0c;移动硬盘作为数据存储与传输的重要工具&#xff0c;其稳定性与安全性直接关系到用户资料的安全。然而&#xff0c;不少用户遭遇过这样一个令人头疼的问题——移动硬盘在接入电脑后&#xff0c;系统突然…