BSTree二叉树讲解

二叉搜索树的概念:

        二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:

  •                 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  •                 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  •                 它的左右子树也分别为二叉搜索树

二叉树的运用:(改代码就是KV模型的二叉搜索树)

1. K模型K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到
的值。
        比如:给一个单词word,判断该单词是否拼写正确,具体方式如下:
以词库中所有单词集合中的每个单词作为key,构建一棵二叉搜索树


在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。
2. KV模型每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对。该种方
式在现实生活中非常常见:
        比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英
文单词与其对应的中文<word, chinese>就构成一种键值对;
        再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出
现次数就是<word, count>就构成一种键值对。

二叉树的查找:

        a、从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找。
        b、最多查找高度次,走到到空,还没找到,这个值不存在。

	Node* _Find(Node* root,const K& key)
	{
		if (root == nullptr)//遇到空节点就返回
		{
			return nullptr;
		}
		if (root->_kv.first == key)
		{
			return root;
		}
		else if (root->_kv.first > key)
		{
			_Find(root->_left, key);
		}
		else
		{
			_Find(root->_right, key);
		}
	}

 二叉搜索树的插入:

插入的具体过程如下:
        a. 树为空,则直接新增节点,赋值给root指针
        b. 树不空,按二叉搜索树性质查找插入位置,插入新节点

	bool _Insert(const pair<K, V>& kv)//这里使用pair——key,value模型 如字典
	{
		if (_root == nullptr)//如果该二叉树的树为空 就给个节点然后返回
		{
			_root = new Node(kv);
			return true;
		}
        //不为空就进行寻找 找到太该呆的位置 
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_kv.first > kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (cur->_kv.first < kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else
			{
				return false;
			}
		}
        //判断该节点在父节点的左边还是右边
		if (parent->_kv.first > kv.first)
		{
			parent->_left = new Node(kv);
		}
		else
		{
			parent->_right = new Node(kv);
		}
	}
	Node* _root = nullptr;
};

二叉搜索树的删除:

首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情
况:
        a. 要删除的结点无孩子结点
        b. 要删除的结点只有左孩子结点
        c. 要删除的结点只有右孩子结点
        d. 要删除的结点有左、右孩子结点
看起来有待删除节点有4中情况,实际情况a可以与情况b或者c合并起来,因此真正的删除过程
如下:
        情况b:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点--直接删除
        情况c:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点--直接删除
        情况d:在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点
        中,再来处理该结点的删除问题--替换法删除

	bool _Erase(Node* root, const K& key)
	{
		Node* parent = nullptr;
		Node* cur = root;
		while (cur)
		{
			if (cur->_kv.first == key)//如果key与要搜索的值相等 就进行删除
			{
				if (cur->_left == nullptr)//左子树为空 右子树不为空
				{//再考虑要删除的节点是他父节点的左子树还是右子树 将该节点接到其父节点上
					if (cur == parent->_left)
					{
						parent->_left = cur->_right;
					}
					else
					{
						parent->_right = cur->_right;
					}
					delete cur;//将该节点进行释放
				}
				else if (cur->_right == nullptr)//右子树不存在 左子树存在 同上
				{
					if (cur == parent->_left)
					{
						parent->_left = cur->_left;
					}
					else
					{
						parent->_right = cur->_left;
					}
					delete cur;
				}
				else//左右子树都存在 需要寻找一个可以代替该节点值的值 
				{//在左子树中找最小的值 (就是左子树最左边的那个节点)右节点就找右子树中最大的节点 (就是右子树最右边的那个节点)
					Node* parent1 = nullptr;
					Node* tmp = cur;
					while (tmp->_left)//寻找左节点
					{
						parent1 = tmp;
						tmp = tmp->_left;
					}
					swap(&cur->_kv, &tmp->_kv);//找到进行值的交换
					parent1->_left = tmp->_right;//如果最小的左节点的左子树为空右子树不为空 将右子树接到该节点的父亲上 但是如果右子树为空 也可以将空节点接到其父节点上 不产生影响
					delete tmp;//释放节点
				}
			}
			else
			{
				if (root->_kv.first > key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else
				{
					parent = cur;
					cur = cur->_right;
				}
			}
		}
	}

二叉树的遍历:(中序遍历)(将二叉树中key的值按顺序打印)

	void _InOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}
		_InOrder(root->_left);
		cout << root->_kv.first << " " << root->_kv.second << endl;
		_InOrder(root->_right);
	}

以下就是二叉搜索树的完整代码 以及对应的测试用例,可以自行去调用测试。

#include<iostream>
#include<string>
using namespace std;

template<class K,class V>
struct BSTreeNode
{
	typedef BSTreeNode<K, V> Node;

	BSTreeNode(const pair<K, V>& x)
		:_kv(x)
		, _left(nullptr)
		, _right(nullptr)
	{

	}
	pair<K, V> _kv;
	Node* _left;
	Node* _right;
};

template<class K, class V>
class BSTree
{
	typedef BSTreeNode<K, V> Node;
public:
	bool Insert(const K& key, const V& value)
	{
		pair<K, V> kv(key, value);
		return _Insert(kv);
	}
	Node* Find(const K& key)
	{
		return _Find(_root,key);
	}
	bool Erase(const K& key)
	{
		return _Erase(_root, key);
	}
	void InOrder()
	{
		_InOrder(_root);
	}
private:
	bool _Erase(Node* root, const K& key)
	{
		Node* parent = nullptr;
		Node* cur = root;
		while (cur)
		{
			if (cur->_kv.first == key)
			{
				if (cur->_left == nullptr)
				{
					if (cur == parent->_left)
					{
						parent->_left = cur->_right;
					}
					else
					{
						parent->_right = cur->_right;
					}
					delete cur;
				}
				else if (cur->_right == nullptr)
				{
					if (cur == parent->_left)
					{
						parent->_left = cur->_left;
					}
					else
					{
						parent->_right = cur->_left;
					}
					delete cur;
				}
				else
				{
					Node* parent1 = nullptr;
					Node* tmp = cur;
					while (tmp->_left)
					{
						parent1 = tmp;
						tmp = tmp->_left;
					}
					swap(&cur->_kv, &tmp->_kv);
					parent1->_left = tmp->_right;
					delete tmp;
				}
			}
			else
			{
				if (root->_kv.first > key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else
				{
					parent = cur;
					cur = cur->_right;
				}
			}
		}
	}
	void _InOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}
		_InOrder(root->_left);
		cout << root->_kv.first << " " << root->_kv.second << endl;
		_InOrder(root->_right);
	}
	Node* _Find(Node* root,const K& key)
	{
		if (root == nullptr)
		{
			return nullptr;
		}
		if (root->_kv.first == key)
		{
			return root;
		}
		else if (root->_kv.first > key)
		{
			_Find(root->_left, key);
		}
		else
		{
			_Find(root->_right, key);
		}
	}
	bool _Insert(const pair<K, V>& kv)
	{
		if (_root == nullptr)
		{
			_root = new Node(kv);
			return true;
		}
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_kv.first > kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (cur->_kv.first < kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else
			{
				return false;
			}
		}
		if (parent->_kv.first > kv.first)
		{
			parent->_left = new Node(kv);
		}
		else
		{
			parent->_right = new Node(kv);
		}
	}
	Node* _root = nullptr;
};

void TestBSTree()
{
	BSTree<string, string> dict;
	dict.Insert("insert", "插入");
	dict.Insert("erase", "删除");
	dict.Insert("left", "左边");
	dict.Insert("string", "字符串");

	string str;
	while (cin >> str)
	{
		auto ret = dict.Find(str);
		if (ret)
		{
			cout << str << ":" << ret->_kv.second << endl;
		}
		else
		{
			cout << "单词拼写错误" << endl;
		}
	}

	string strs[] = { "苹果", "西瓜", "苹果", "樱桃", "苹果", "樱桃", "苹果", "樱桃", "苹果" };
	// 统计水果出现的次
	BSTree<string, int> countTree;
	for (auto str : strs)
	{
		auto ret = countTree.Find(str);
		if (ret == NULL)
		{
			countTree.Insert(str, 1);
		}
		else
		{
			ret->_kv.second++;
		}
	}
	countTree.InOrder();
}

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

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

相关文章

【MATLAB】安装Psychtoolbox

目录 一、下载Psychtoolbox工具包 1. 一个是这个ZTP文件 2. 分别下载 Subversion 1.7.x command-line client 和 gstreamer.freedesktop.org 二、解压工具包&#xff0c;保存至同一文件 三、安装到matlab 1. 安装psychtoolbox 2. 检查是否安装成功 一、下载Psychtoolbox…

k8s statefulSet 学习笔记

文章目录 缩写: sts创建sts扩缩容金丝雀发布OnDelete 删除时更新 缩写: sts 通过 kubectl api-resources 可以查到&#xff1a; NAMESHORTNAMESAPIVERSIONNAMESPACEDKINDstatefulsetsstsapps/v1trueStatefulSetweb-sts.yaml apiVersion: v1 kind: Service metadata:name: ng…

[UDS] --- CommunicationControl 0x28

1 0x28功能描述 根据ISO14119-1标准中所述&#xff0c;诊断服务28服务主要用于网络中的报文发送与接受&#xff0c;比如控制应用报文的发送与接收&#xff0c;又或是控制网络管理报文的发送与接收&#xff0c;以便满足一定场景下的应用需求。 2 0x28应用场景 一般而言&#…

MAC安装stable diffusion

电脑配置 基本安装 1. 安装python 2. 安装git 3. 下载stable diffusion的代码&#xff0c;地址&#xff1a; git clone https://github.com/AUTOMATIC1111/stable-diffusion-webui 执行命令 ./webui.sh --precision full --no-half-vae --disable-nan-check --api Command…

2023年阿里云双11优惠来了,单笔最高可省2400元!

2023年阿里云双11活动终于来了&#xff0c;阿里云推出了金秋云创季活动&#xff0c;新用户、老用户、企业用户均可领取金秋上云礼包&#xff0c;单笔最高立减2400元&#xff01; 一、活动时间 满减券领取时间&#xff1a;2023年10月27日0点0分0秒-2023年11月30日23点59分59秒 …

商业模式画布的9大模块全解读,产品经理不可不知!

“商场如战场”&#xff0c;在当今瞬息万变的商业环境中&#xff0c;创造出独特且创新的商业模式是每个企业家、策略家和决策者的首要任务。为了在激烈的市场竞争中取得优势&#xff0c;我们需要一个强大且直观的工具来帮助我们规划和塑造公司的商业模式&#xff0c;这个经常被…

Websocket传递JWT令牌

在访问带有[Authorize]的方法的时候&#xff0c;需要前端通过自定义报文头的形式将JWT令牌传递给后端进行验证&#xff0c;否则是不能访问带有[Authorize]的方法。 [Authorize]是用于限制对web应用程序中某些操作或控制器的访问。当[授权]属性应用于操作或控制器时&#xff0c;…

生物信息学分析-blast序列比对及结果详细说明

1. 软件说明 Blast是一种基于序列比对的分析工具&#xff0c;可以用于寻找生物序列之间的同源性&#xff0c;它的全称是Basic Local Alignment Search Tool。 Blast有多种版本和用途&#xff0c;最常见的是基于Web的Blast和本地安装的Blast程序。Web版Blast可以直接在NCBI网站…

【MATLAB源码-第62期】基于蜣螂优化算法(DBO)的无人机三维地图路径规划,输出最短路径和适应度曲线。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 蜣螂优化算法&#xff08;Dung Beetle Optimization, DBO&#xff09;是一种模拟蜣螂在寻找食物和进行导航的过程的优化算法。蜣螂是一种能够将粪球滚到合适地点的昆虫&#xff0c;它们利用天空中的光线和自身的感知能力来确…

【EI会议征稿】第三届绿色能源与电力系统国际学术会议(ICGEPS 2024)

第三届绿色能源与电力系统国际学术会议&#xff08;ICGEPS 2024&#xff09; 2024 3rd International Conference on Green Energy and Power Systems 绿色能源是指可以直接用于生产和生活的能源。它包括核能和“可再生能源”。随着世界各国能源需求的不断增长和环境保护意识…

一文告诉你样机是什么,分享几个常用的样机模板

一个项目的诞生通常需要经历头脑构思、绘制设计和最终着陆。在这个过程中&#xff0c;样机制作往往是在着陆实践之前进行的。俗话说&#xff1a;“样机使用得好&#xff0c;草稿过早”。样机设计是产品或网站最终设计的生动、静态和视觉表现。它为用户提供了一种模拟现实的方式…

表白墙/留言墙 —— 初级SpringBoot项目,练手项目前后端开发(带完整源码) 全方位全步骤手把手教学

&#x1f9f8;欢迎来到dream_ready的博客&#xff0c;&#x1f4dc;相信你对这篇博客也感兴趣o (ˉ▽ˉ&#xff1b;) 用户登录前后端开发(一个简单完整的小项目)——SpringBoot与session验证&#xff08;带前后端源码&#xff09;全方位全流程超详细教程 目录 项目前端页面展…

算法的时间复杂度及空间复杂度

目录 一、前言 二、时间复杂度 1.时间复杂度定义 2.时间复杂度描述方法 三、实例代码 实例1&#xff08;取影响最大的项&#xff09; 实例2&#xff08;舍去系数&#xff09; 实例3&#xff08;不确定大小关系的用max函数取最大&#xff09; 实例4&#xff08;常数次的…

Windows原生蓝牙编程 第二章 选取设备输入配对码并配对【C++】

蓝牙系列文章目录 第一章 获取本地蓝牙并扫描周围蓝牙信息并输出 第二章 选取设备输入配对码并配对 文章目录 前言头文件一、选择想要配对的设备并设置配对码1.1 设置配对码1.2 选择设备并配对 二、全部代码三、测试结果总结 前言 接着第一章&#xff0c;我们已经把扫描到的蓝…

Leetcode 43. 字符串相乘 中等

题目 - 点击直达 1. 43. 字符串相乘 中等1. 题目详情1. 原题链接2. 题目要求3. 基础框架 2. 思路一 做加法1. 思路分析2. 时间复杂度3. 代码实现 3. 思路二 做乘法1. 思路分析2. 时间复杂度3. 代码实现 1. 43. 字符串相乘 中等 1. 题目详情 给定两个以字符串形式表示的非负整…

Acrobat Pro DC 2023 PDF编辑器 for Mac

Acrobat Pro DC是一款由Adobe开发的专业级PDF编辑和管理软件。作为PDF行业的标准工具&#xff0c;它提供了广泛的功能和工具&#xff0c;适用于个人用户、企业和专业人士。 Acrobat Pro DC具备丰富的编辑功能&#xff0c;可以对PDF文件进行文本编辑、图像编辑和页面重排等操作。…

订水商城H5实战教程-05权限控制

目录 1 判断用户是否登录2 创建事件流3 获取不到Userid的问题4 权限控制整体效果 我们上一篇讲解了用户注册的功能&#xff0c;当用户注册完毕的时候再次打开小程序的时候就需要验证权限。权限分为两类&#xff0c;第一类是判断用户是否注册&#xff0c;第二类是当前用户具备什…

Linux启动之uboot分析

Linux启动之uboot分析 uboot是什么&#xff1f;一、补充存储器概念1.存储器种类1.norflash - 是非易失性存储器&#xff08;也就是掉电保存&#xff09;2.nandflash - 是非易失性存储器&#xff08;也就是掉电保存&#xff09;3.SRAM - 静态随机访问存储器 - Static Random Acc…

什么是鉴权?一篇文章带你了解postman的多种方式

一、什么是鉴权&#xff1f; 鉴权也就是身份认证&#xff0c;就是验证您是否有权限从服务器访问或操作相关数据。发送请求时&#xff0c;通常必须包含相应的检验参数以确保请求具有访问权限并返回所需数据。通俗的讲就是一个门禁&#xff0c;您想要进入室内&#xff0c;必须通…

MySQL(3):基本的 SELECT 语句

SQL 语言 SQL&#xff08;Structured Query Language&#xff0c;结构化查询语言&#xff09;是使用关系模型的数据库应用语言&#xff0c; 与数据直接打交道 。 SQL 有两个重要的标准&#xff0c;分别是 SQL92 和 SQL99&#xff0c;它们分别代表了 92 年和 99 年颁布的 SQL 标…