【二叉树进阶】二叉搜索树

目录

1. 二叉搜索树概念

2. 二叉搜索树的实现

2.1 创建二叉搜索树节点

2.2 创建实现二叉搜索树

2.3 二叉搜索树的查找

2.4 二叉搜索树的插入

2.5 二叉搜索树的删除

2.6 中序遍历

2.7 完整代码加测试

3. 二叉搜索树的应用

3.1 K模型:

3.2 KV模型:

4. 二叉搜索树的性能分析


1. 二叉搜索树概念

二叉搜索树(BST,Binary Search Tree):可以是一颗空树,满足以下性质:

  • 根节点的值大于左子树上所有节点的值,小于右子树上所有节点的值。
  • 它的左子树和右子树也分别为二叉搜索树。

它的中序遍历是有序的:0 1 2 3 4 5 6 7 8 9 ,所以也称为二叉排序树或二叉查找树。

2. 二叉搜索树的实现

int a[ ] = { 5, 3, 4, 1, 7, 8, 2, 6, 0, 9 };

2.1 创建二叉搜索树节点

template<class K>
struct BSTreeNode
{BSTreeNode* _left;BSTreeNode* _right;K _key;//初始化节点BSTreeNode(const K& key):_left(nullptr), _right(nullptr), _key(key){}
};

2.2 创建实现二叉搜索树

template<class K>
class BSTree
{typedef BSTreeNode<K> Node;
public://操作实现//查找bool Find(const K& key) { }//插入void Insert(const K& key) { }//删除bool Erase() { }//中序遍历void InOrder() { }
private:Node* _root = nullptr;
}

2.3 二叉搜索树的查找

a、从根开始查找,比根大往右边查找,比根小往左边查找。

b、最多查找高度次,走到空,还没找到,则要查找的值不存在。

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

2.4 二叉搜索树的插入

a、如果树为空,新增节点,赋值给_root指针。

b、按搜索二叉树性质查找插入位置,插入节点。

c、要插入的位置一定为为空的位置,所以要插入,还要找到要插入位置的父节点,让父节点指向新插入的节点。

bool Insert(const K& key)
{//如果树为空,新增节点if (_root == nullptr){_root = new Node(key);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){//比根小if (key < cur->_key){parent = cur;cur = cur->_left;}//比根大else if (key > cur->_key){parent = cur;cur = cur->_right;}//与根相等else{return false;}}cur = new Node(key);if (key < parent->_key){parent->_left = cur;}else{parent->_right = cur;}return true;
}

2.5 二叉搜索树的删除

a、先判断要删除的元素是否存在,不存在,返回false。

b、若存在,则分为以下情况:

  1. 要删除的节点(叶子节点)左右子树为空
  2. 要删除的节点左子树为空或右子树为空
  3. 要删除的节点左子树右子树都不为空

实际操作起来,情况1和情况2能合并到一起实现,只有两种情况:

  • 要删除的节点的左子树或右子树为空:直接删除-----让父亲指向要删除节点右子树(左子树),再直接删除该节点。

如果要删除的该节点为根,则直接让它的右子树(左子树)赋值给_root让它成为新根即可。

  • 要删除的节点的左右子树都不为空:替换法删除----在它的右子树中找到最小值(最左节点)或在左子树中找到最大值(最右节点),将它的值与要删除节点的值替换,这时就转换为该节点的删除问题。

右子树最左节点的左子树一定为空,左子树最右节点的右子树一定为空,这就转换为第一种情况的删除了。

	bool Erase(const K& key){Node* parent = nullptr;Node* cur = _root;//查找while (cur){if (key < cur->_key){parent = cur;cur = cur->_left;}else if (key > cur->_key){parent = cur;cur = cur->_right;}//找到了 删除else{//1.要删除节点的左子树为空或者右子树为空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;}}}//要删除的左右子树都不为空else{//这里找到是右子树的最左值Node* rightMinParent = cur;Node* rightMin = cur->_right;//要替换的节点while (rightMin->_left){rightMinParent = rightMin;rightMin = rightMin->_left;}//替换法交换cur->_key = rightMin->_key;//转换成删除rightMinif (rightMin == rightMinParent->_right){//rightMin的左子树一定为空rightMinParent->_right = rightMin->_right;}else{rightMinParent->_left = rightMin->_right;}delete rightMin;}//这里我们去找左子树的最大值//else//{//	Node* leftMaxParent = cur;//	Node* leftMax = cur->_left;//	while (leftMax->_right)//	{//		leftMaxParent = leftMax;//		leftMax = leftMax->_right;//	}//	//替换法删除//	cur->_key = leftMax->_key;//	//转换为删除leftMax//	if (leftMax == leftMaxParent->_left)//	{//		leftMaxParent->_left = leftMax->_left;//	}//	else//	{//		leftMaxParent->_right = leftMax->_left;//	}//	delete leftMax;//}return true;}}return false;}

2.6 中序遍历

中序遍历:按照左子树 根 右子树的方式迭代遍历二叉树。

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

总之,我们在实现二叉搜索树的时候一定要考虑多种情况,每种情况也要多找几个节点进行分析。

2.7 完整代码加测试

BSTree.hpp:

//创建二叉树节点
template<class K>
struct BSTreeNode
{BSTreeNode* _left;BSTreeNode* _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){//如果树为空,新增节点if (_root == nullptr){_root = new Node(key);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){//比根小if (key < cur->_key){parent = cur;cur = cur->_left;}//比根大else if (key > cur->_key){parent = cur;cur = cur->_right;}//与根相等else{return false;}}cur = new Node(key);if (key < parent->_key){parent->_left = cur;}else{parent->_right = cur;}return true;}//中序遍历void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);}void InOrder(){_InOrder(_root);cout << endl;}bool Erase(const K& key){Node* parent = nullptr;Node* cur = _root;//查找while (cur){if (key < cur->_key){parent = cur;cur = cur->_left;}else if (key > cur->_key){parent = cur;cur = cur->_right;}//找到了 删除else{//1.要删除节点的左子树为空或者右子树为空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;}}}//要删除的左右子树都不为空else{//这里找到是右子树的最左值Node* rightMinParent = cur;Node* rightMin = cur->_right;//要替换的节点while (rightMin->_left){rightMinParent = rightMin;rightMin = rightMin->_left;}//替换法交换cur->_key = rightMin->_key;//转换成删除rightMinif (rightMin == rightMinParent->_right){//rightMin的左子树一定为空rightMinParent->_right = rightMin->_right;}else{rightMinParent->_left = rightMin->_right;}delete rightMin;}//这里我们去找左子树的最大值//else//{//	Node* leftMaxParent = cur;//	Node* leftMax = cur->_left;//	while (leftMax->_right)//	{//		leftMaxParent = leftMax;//		leftMax = leftMax->_right;//	}//	//替换法删除//	cur->_key = leftMax->_key;//	//转换为删除leftMax//	if (leftMax == leftMaxParent->_left)//	{//		leftMaxParent->_left = leftMax->_left;//	}//	else//	{//		leftMaxParent->_right = leftMax->_left;//	}//	delete leftMax;//}return true;}}return false;}bool Find(const K& key){if (_root == nullptr){return false;}Node* cur = _root;while (cur){if (key < cur->_left){cur = cur->_left;}else if (key > cur->_right){cur = cur->_right;}else{return true;}}return false;}
private:Node* _root = nullptr;
};
void TestBSTree()
{BSTree<int> t;int a[] = { 5, 3, 4, 1, 7, 8, 2, 6, 0, 9 };for (auto e : a){t.Insert(e);}for (auto e : a){t.Erase(e);t.InOrder();}
}

Test.cpp:

#include"BSTree.hpp"
int main()
{TestBSTree();return 0;
}

运行结果:

3. 二叉搜索树的应用

3.1 K模型:

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

比如:给一个单词,判断该单词是否正确:

  • 以词库中所有单词集合中的每个单词作为key,构建一颗二叉搜索树
  • 在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。

其实就是上面我们实现的二叉搜索树的查找。这里不在重复实现了。

3.2 KV模型:

每一个关键码Key,都有与子对应的Value,即<Key,Value>的键值对。

  • 比如:英汉词典就是英文与中文的对应关系,通过英文可以快速找到对应中文,英文单词与其中文构成<world,chinese>就构成一种键值对;
  • 比如:统计单词次数,统计成功后,给定单词可以快速找到其出现次数,单词与其出现次数<world,count>就构成一种键值对。

也很简单,其实就是让二叉搜索树中的节点多存一个值Value,查找、插入、删除等操作依旧是按照key去实现的。

改造二叉搜索树为KV结构模型:

template<class K,class V>
struct BSTreeNode
{BSTreeNode<K,V>* _left;BSTreeNode<K,V>* _right;K _key;V _value;//多存一个值valueBSTreeNode(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){ }void _InOrder(Node* root){ }void InOrder(){ }bool Erase(const K& key){ }Node* Find(const K& key)//返回节点的指针,这里不再实现{ }
private:Node* _root = nullptr;
};

应用测试1:输入英文查找对应的中文

void TestBSTree()
{BSTree<string, string> dict;dict.Insert("string", "字符串");dict.Insert("tree", "树");dict.Insert("int", "整型");dict.Insert("search", "查找");dict.Insert("insert", "插入");string str;while (cin >> str){BSTreeNode<string, string>* ret = dict.Find(str);if (ret == nullptr){cout << "词库中无此单词" << endl;}cout << ret->_value << endl;}
}

结果:

应用测试2:查找水果出现的次数

void TestBSTree()
{string arr[] = { "苹果","香蕉","西瓜","西瓜","香梨","西瓜" ,"苹果" ,"西瓜" ,"西瓜" ,"香蕉" ,"苹果" };BSTree<string, int> t;int i = 0;for (auto e : arr){BSTreeNode<string, int>* ret = t.Find(e);//ret为空说明要查找的是第一次出现if (ret == nullptr){t.Insert(e, 1);}else{ret->_value++;}}t.InOrder();
}

结果:

4. 二叉搜索树的性能分析

插入、删除操作都必须先查找,所有查找效率代表了二叉搜索树中各个操作的性能。

对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二
叉搜索树的深度的函数,即结点越深,则比较次数越多。
但对于同一个关键码集合,如果各关键码插入的次序不同(无序、接近有序),可能得到不同结构的二叉搜索树:

  • 最优情况下,二叉搜索树为完全二叉树(或者接近完全二叉树),其平均比较次数为:log2N(以2为底N的对数)。
  • 最差情况下,二叉搜索树退化为单支树(或者类似单支),其平均比较次数为:N。
如果为单支树,二叉搜索树的性能就没有了,那么这种情况就要用到AVL树和红黑树了,后续再讲解。

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

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

相关文章

数据技术革命来袭!从仓库到飞轮,企业数字化的终极进化!

文章目录 数据仓库&#xff1a;信息化的基石数据中台&#xff1a;数字化转型的加速器数据飞轮&#xff1a;智能化的新纪元技术演进的驱动力 自20世纪80年代末数据仓库问世以来&#xff0c;它迅速成为企业数据管理的核心。作为一名大数据工程师&#xff0c;我深刻体会到数据仓库…

k8s使用本地docker私服启动自制的flink集群

目标&#xff1a;使用本地flink环境自制flink镜像包上传到本地的私服&#xff0c;然后k8s使用本地的私服拉取镜像启动Flink集群 1、将本地的flink软件包打包成Docker镜像 从官网下载flink-1.13.6的安装包&#xff0c;修改其中的flink-conf.yaml&#xff0c;修改下面几项配置 …

Mistral AI再创新高,Pixtral 12B多模态模型强势来袭

前沿科技速递&#x1f680; 近日&#xff0c;Mistral AI 发布了其首款多模态大模型——Pixtral 12B。作为一款具有语言与视觉处理能力的模型&#xff0c;Pixtral 12B 支持高达10241024像素的图像&#xff0c;具备强大的文本生成、图像理解与生成能力&#xff0c;能够处理复杂的…

热成像目标检测数据集

热成像目标检测数据集 V2 版本 项目背景 热成像技术因其在安防监控、夜间巡逻、消防救援等领域的独特优势而受到重视。本数据集旨在提供高质量的热成像图像及其对应的可见光图像&#xff0c;支持热成像目标检测的研究与应用。 数据集概述 名称&#xff1a;热成像目标检测数据…

Kafka日志索引详解与常见问题分析

目录 一、Kafka的Log日志梳理 1、Topic下的消息是如何存储的&#xff1f; 1. log文件追加记录所有消息 2. index和timeindex加速读取log消息日志 2、文件清理机制 1. 如何判断哪些日志文件过期了 2. 过期的日志文件如何处理 3、Kafka的文件高效读写机制 1. Kafka的文件…

图神经网络模型扩展(5)--2

1.图的无监督学习 在数据爆炸的时代&#xff0c;大部分数据都是没有标签的。为了将它们应用到深度学习模型上&#xff0c;需要大量的人力来标注数据&#xff0c;例如我们熟知的人脸识别项目&#xff0c;如果想取得更好的识别效果&#xff0c;则一定需要大量人工标注的人脸数据。…

Android MediaPlayer + GLSurfaceView 播放视频

Android使用OpenGL 播放视频 概述TextureView的优缺点OpenGL的优缺点 实现复杂图形效果的场景参考 概述 在Android开发中&#xff0c;使用OpenGL ES来渲染视频是一种常见的需求&#xff0c;尤其是在需要实现自定义的视频播放界面或者视频特效时。结合MediaPlayer&#xff0c;我…

【论文阅读】BC-Z: Zero-Shot Task Generalization with Robotic Imitation Learning

Abstract 在这篇论文中&#xff0c;我们研究了使基于视觉的机器人操纵系统能够泛化到新任务的问题&#xff0c;这是机器人学习中的一个长期挑战。我们从模仿学习的角度来应对这一挑战&#xff0c;旨在研究如何扩展和扩大收集的数据来促进这种泛化。为此&#xff0c;我们开发了…

数据库之索引<保姆级文章>

目录&#xff1a; 一. 什么是索引 二. 索引应该选择哪种数据结构 三. MySQL中的页 四. 索引分类及使用 一. 什么是索引&#xff1a; 1. MySQL的索引是⼀种数据结构&#xff0c;它可以帮助数据库高效地查询、更新数据表中的数据。 索引通过 ⼀定的规则排列数据表中的记录&#x…

F28335 时钟及控制系统

1 F28335 系统时钟来源 1.1 振荡器OSC与锁相环PLL 时钟信号对于DSP来说是非常重要的,它为DSP工作提供一个稳定的机器周期从而使系统能够正常运行。时钟系统犹如人的心脏,一旦有问题整个系统就崩溃。DSP 属于数字信号处理器, 它正常工作也必须为其提供时钟信号。那么这个时钟…

【例题】lanqiao3225 宝藏排序Ⅰ

这里的n的范围可以使用冒泡排序、选择排序和插入排序等算法。 冒泡排序 nint(input()) alist(map(int,input().split()))def pop_sort(a):for i in range(n):for j in range(n-i-1):if a[j]>a[j1]:a[j],a[j1]a[j1],a[j] pop_sort(a) print( .join(map(str,a)))选择排序 n…

数据结构(7.3_2)——平衡二叉树

平衡二叉树&#xff0c;简称平衡树(AVL树)----树上任一结点的左子树和右子树的高度之差不超过1. 结点的平衡因子左子树高-右子树高 //平衡二叉树结点 typedef struct AVLNode {int key;//数据域int blalance;//平衡因子struct AVLNode* lchild, * rchild; }AVLNode,*AVLTree; …

4. Python之运算符

一. Python运算符 常用的运算符有&#xff1a;算述运算符&#xff0c;赋值运算符&#xff0c;比较运算述&#xff0c;逻辑运算符&#xff0c;位运算符等等。 1. 算述运算符 用于处理四则运算的符号&#xff0c;主要有&#xff1a; 运算符描述加法-减法*乘法/除法//整除%取余…

Nature Climate Change | 全球土壤微生物群落调控微生物呼吸对变暖的敏感性(Q10)

本文首发于“生态学者”微信公众号&#xff01; 全球变暖将加速有机物分解&#xff0c;从而增加土壤中二氧化碳的释放&#xff0c;触发正的碳-气候反馈。这种反馈的大小在很大程度上取决于有机质分解的温度敏感性(Q10)。Q10仍然是围绕土壤碳排放到大气的预测的主要不确定性来源…

FreeRTOS实战指南 — 3.2 FreeRTOS中链表的实现

目录 1 FreeRTOS中链表的实现 1.1 实现链表节点 1.2 实现链表根节点 1.3 将节点插入到链表的尾部 1.4 将节点按照升序排列插入到链表 1.5 将节点从链表删除 1.6 节点带参宏小函数 2 链表操作实验 1 FreeRTOS中链表的实现 1.1 实现链表节点 在FreeRTOS操作系统中&…

第二界陇剑杯赛-MISC

1 题目名称&#xff1a;hard_web-1 题目内容&#xff1a;1.服务器开放了哪些端口&#xff0c;请按照端口大小顺序提交答案&#xff0c;并以英文逗号隔开(如服务器开放了80 81 82 83端口&#xff0c;则答案为80,81,82,83) 题目分值&#xff1a;100.0 题目难度&#xff1a;容易 …

go语言中的数组指针和指针数组的区别详解

1.介绍 大家知道C语言之所以强大&#xff0c;就是因为c语言支持指针&#xff0c;而且权限特别大&#xff0c;c语言可以对计算机中任何内存的指针进行操作&#xff0c;这样自然而然也会带来一些不安全的因素&#xff0c;所以在golang中&#xff0c;「取消了对指针的一些偏移&…

自动排课管理系统(源代码+论文+开题报告)

一、题目摘要 题目简要说明&#xff1a; 选排课系统功能的设计上&#xff0c;选排课系统可以分为登录、排课和选课3个子系统。登录子系统区分排课者(也即系统的管理者)、教师和学生这三者的不同身份&#xff0c;给出不同的权限&#xff0c;在页面中根据身份判断其相应具有的功…

战斗机检测系统源码分享

战斗机检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Visio…

【K230 实战项目】气象时钟

【CanMV K230 AI视觉】 气象时钟 功能描述&#xff1a;说明HMDI资源3.5寸屏幕 使用方法 为了方便小伙伴们理解&#xff0c;请查看视频 B站连接 功能描述&#xff1a; 天气信息获取&#xff1a;通过连接到互联网&#xff0c;实时获取天气数据&#xff0c;包括温度、湿度、天气状…