C++笔记17•数据结构:二叉搜索树(K模型/KV模型实现)•

news2024/9/24 5:31:33

二叉搜索树

1.二叉搜索树

1. 二叉搜索树的查找
a 、从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找。
b 、最多查找高度次,走到到空,还没找到,这个值不存在。
2. 二叉搜索树的插入
插入的具体过程如下:
a. 树为空,则直接新增节点,赋值给 root 指针
b. 树不空,按二叉搜索树性质查找插入位置,插入新节点
3.二叉搜索树的删除
首先查找元素是否在二叉搜索树中,如果不存在,则返回 , 否则要删除的结点可能分下面四种情 况:
a. 要删除的结点无孩子结点
b. 要删除的结点只有左孩子结点
c. 要删除的结点只有右孩子结点
d. 要删除的结点有左、右孩子结点
看起来有待删除节点有 4 中情况,实际情况 a 可以与情况 b 或者 c 合并起来,因此真正的删除过程如下:
情况 b :删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点 -- 直接删除
情况 c :删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点 -- 直接删除
情况 d:在它的右子树中寻找中序下的第一个结点( 也就是删除节点的左子树中最大的值或者删除节点的右子树中最小的值 ),用它的值填补到被删除节点 中,再来处理该结点的删除问题 -- 替换法删除

删除9、16、3、10节点

其中:节点9和16可以直接删除。3、10节点需要用替换法删除

节点3:需要用2节点或7节点来替换

节点10:需要用9节点或12节点来替换

 2.二叉搜索树-K模型

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;

//二叉搜索树BinarySearchTree
//struct BinarySearchTreeNode

template<class K>
struct BSTreeNode
{
	BSTreeNode<K>* _left;//一定不要写成BSTreeNode*<K>  _left;  这样编译器无法识别
	BSTreeNode<K>* _right;
	K _key;

	BSTreeNode(const K& key)
		:_left(nullptr)
		, _right(nullptr)
		, _key(key)
	{}
};

template<class K>
class BSTree
{
	typedef struct 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 (cur->_key < key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_key > key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false;
			}
		}

		//准备从parent插入
		Node* node = new Node(key);
		if (parent->_key > key)//插左子树
		{
			parent->_left = node;
		}
		else//插右子树
		{
			parent->_right = node;
		}
		//cur = new Node(key);
		//if (parent->_key > 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;
	}
	void find(const K& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key == key)
			{
				cout << "找到了!" << endl;
				return;
			}
			else if (cur->_key > key)
			{
				cur = cur->_left;
			}
			else if (cur->_key < key)
			{
				cur = cur->_right;
			}
		}
		cout << "找不到!" << endl;
		return;
	}
	bool erase(const K& key)
	{
		Node* parent = nullptr;
		Node* cur = _root;

		while (cur)
		{
			if (cur->_key > key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (cur->_key < key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else   //找到了开始删除  1.左为空  2.右为空  3.左右都不为空
			{
				if (cur->_left == nullptr) //1.左为空
				{
					if (cur == _root) //判断删除的节点是否是根节点 根的左为空 让根的右成为根就可以了 _root = cur->_right
					{
						_root = cur->_right;
					}
					else
					{
						if (parent->_left == cur)
						{
							parent->_left = cur->_right;
						}
						else     //parent->_right == cur
						{
							parent->_right = cur->_right;
						}
					}
					delete cur;                   //不要忘记手动释放节点
				}
				else if (cur->_right == nullptr)//2.右为空
				{
					if (cur == _root)//判断删除的节点是否是根节点 根的右为空 让根的左成为根就可以了 _root = cur->_left
					{
						_root = cur->_left;
					}
					else
					{
						if (parent->_left == cur)
						{
							parent->_left = cur->_left;
						}
						else   //parent->_right == cur
						{
							parent->_right = cur->_left;
						}
					}
					delete cur;
				}
				else   //3.左右都不为空 用删除节点的左子树中最大的值或者删除节点的右子树中最小的值  此处用删除节点的左子树最大值
				{
					//Node* Lbignode_pre = nullptr;//不能置空 后面如果直接跳出循环Lbignode_pre还是空,会出bug;Lbignode_pre->_right  空指针不能这样访问
					Node* Lbignode_pre = cur;

					Node* Lbignode = cur->_left;

					while (Lbignode->_right)
					{
						Lbignode_pre = Lbignode;
						Lbignode = Lbignode->_right;
					}

					cur->_key = Lbignode->_key;//替换节点中的值,删除cur转换为删除Lbignode
					if (Lbignode == Lbignode_pre->_right)
					{
						Lbignode_pre->_right = Lbignode->_left;
					}
					else
					{
						Lbignode_pre->_left = Lbignode->_left;
					}

					delete Lbignode;
				}
				return true;
			}
		}
		return false;

	}

private:
	Node* _root = nullptr;
};

void test1()
{
	BSTree<int> bt;
	bt.insert(1);
	bt.insert(10);
	bt.insert(2);
	bt.insert(5);
	bt.insert(4);
	bt.insert(6);
	bt.insert(8);
	bt.insert(9);
	bt.insert(7);
	bt.insert(3);
	bt.insert(0);
	bt.insert(0);

	bt.Inorder();
	bt.find(8);
	bt.find(20);
	bt.erase(20);
	bt.erase(0);
	bt.erase(10);
	bt.erase(8);

	bt.Inorder();

}
void test2()
{
	char arr[] = { 10,9,8,7,6,5,4,3,2,1,0 };
	BSTree<int> bt;
	for (auto e : arr)
	{
		bt.insert(e);
	}
	cout << "插入:" << endl;
	bt.Inorder();
	cout << "依次删除:" << endl;
	for (auto e : arr)
	{
		bt.erase(e);
		bt.Inorder();
	}
}
int main()
{
	//test1();
	test2();
	return 0;

}

 3.二叉搜索树-KV模型

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string>
using namespace std;

//二叉搜索树BinarySearchTree
//struct BinarySearchTreeNode

template<class K,class V>
struct BSTreeNode
{
	BSTreeNode<K,V>* _left;//一定不要写成BSTreeNode*<K>  _left;  这样编译器无法识别
	BSTreeNode<K,V>* _right;
	K _key;
	V _value;

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

template<class K,class V>
class BSTree
{
	typedef struct BSTreeNode<K,V> Node;
public:
	bool insert(const K& key, const V& value)
	{
		if (_root == nullptr)
		{
			_root = new Node(key, value);
			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;
			}
		}

		//准备从parent插入
		Node* node = new Node(key, value);
		if (parent->_key > key)//插左子树
		{
			parent->_left = node;
		}
		else//插右子树
		{
			parent->_right = node;
		}
		//cur = new Node(key, value);
		//if (parent->_key > key)//插左子树
		//{
		//	parent->_left = cur;
		//}
		//else//插右子树
		//{
		//	parent->_right = cur;
		//}
		return true;
	}
	void _Inorder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}
		_Inorder(root->_left);
		cout << root->_key << ":"<<root->_value << endl;
		_Inorder(root->_right);
	}
	void Inorder()
	{
		_Inorder(_root);
		cout << endl;
	}
	Node* find(const K& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key == key)
			{
				//cout << "找到了!" << endl;
				return cur;
			}
			else if (cur->_key > key)
			{
				cur = cur->_left;
			}
			else if (cur->_key < key)
			{
				cur = cur->_right;
			}
		}
		//cout << "找不到!" << endl;
		return nullptr;
	}
	bool erase(const K& key)
	{
		Node* parent = nullptr;
		Node* cur = _root;

		while (cur)
		{
			if (cur->_key > key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (cur->_key < key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else   //找到了开始删除  1.左为空  2.右为空  3.左右都不为空
			{
				if (cur->_left == nullptr) //1.左为空
				{
					if (cur == _root) //判断删除的节点是否是根节点 根的左为空 让根的右成为根就可以了 _root = cur->_right
					{
						_root = cur->_right;
					}
					else
					{
						if (parent->_left == cur)
						{
							parent->_left = cur->_right;
						}
						else     //parent->_right == cur
						{
							parent->_right = cur->_right;
						}
					}
					delete cur;                   //不要忘记手动释放节点
				}
				else if (cur->_right == nullptr)//2.右为空
				{
					if (cur == _root)//判断删除的节点是否是根节点 根的右为空 让根的左成为根就可以了 _root = cur->_left
					{
						_root = cur->_left;
					}
					else
					{
						if (parent->_left == cur)
						{
							parent->_left = cur->_left;
						}
						else   //parent->_right == cur
						{
							parent->_right = cur->_left;
						}
					}
					delete cur;
				}
				else   //3.左右都不为空 用删除节点的左子树中最大的值或者删除节点的右子树中最小的值  此处用删除节点的左子树最大值
				{
					//Node* Lbignode_pre = nullptr;不能置空 后面如果直接跳出循环Lbignode_pre还是空,会出bug;Lbignode_pre->_right  空指针不能这样访问
					Node* Lbignode_pre = cur;

					Node* Lbignode = cur->_left;

					while (Lbignode->_right)
					{
						Lbignode_pre = Lbignode;
						Lbignode = Lbignode->_right;
					}

					cur->_key = Lbignode->_key;//替换节点中的值,删除cur转换为删除Lbignode
					if (Lbignode == Lbignode_pre->_right)
					{
						Lbignode_pre->_right = Lbignode->_left;
					}
					else
					{
						Lbignode_pre->_left = Lbignode->_left;
					}

					delete Lbignode;
				}
				return true;
			}
		}
		return false;

	}

private:
	Node* _root = nullptr;
};

void test1()
{
	BSTree<string,string> Dictionary;
	Dictionary.insert("apple", "苹果");
	Dictionary.insert("pear", "梨");
	Dictionary.insert("left", "左");
	Dictionary.insert("right", "右");
	string str;
	while (cin >> str)
	{
		BSTreeNode<string, string>* ret = Dictionary.find(str);
		if(ret)
			cout << ret->_value << endl;
		else
			cout << "词典没有此单词!" << endl;
	}
	
}
void test2()
{
	string arr[] = { "徐香猕猴桃","葡萄", "梨", "哈密瓜", "西瓜", "苹果", "橙子", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉"};
	BSTree<string, int> countfruit;
	BSTreeNode<string, int>* ret =nullptr;
	for (auto e : arr)
	{
		//BSTreeNode<string, int>* ret =countfruit.find(e);
		ret = countfruit.find(e);
		if (ret == nullptr)
			countfruit.insert(e, 1);
		else
			ret->_value++;
	}
	delete ret;//这里可能会有双重释放节点 ret属于树节点 应在树中析构函数中进行节点的释放的管理,这里一般不需要自己手动释放,自己释放可能会遇到内存泄漏
	countfruit.Inorder();
	delete ret;
}
int main()
{
	//test1();
	test2();
	return 0;

}

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

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

相关文章

【图解版】Likes Vs Dislikes——C语言提高题【7 kyu】

一、原题 链接&#xff1a;Training on Likes Vs Dislikes | Codewars YouTube had a like and a dislike button, which allowed users to express their opinions about particular content. It was set up in such a way that you cannot like and dislike a video at the…

集成电路学习:什么是PWM脉冲宽度调制

一、PWM&#xff1a;脉冲宽度调制 PWM&#xff0c;全称为脉冲宽度调制&#xff08;Pulse Width Modulation&#xff09;&#xff0c;是一种通过调整脉冲信号的宽度来控制电路输出的技术。在PWM中&#xff0c;信号的输出由一系列等幅值的脉冲组成&#xff0c;这些脉冲的宽度根据…

h5适配iOS——window.open失效

在H5开发中&#xff0c;打开一个新的标签页一般使用window.open。安卓中没啥限制&#xff0c;但是iOS的Safari出于安全限制&#xff0c;想使用该方法打开新的网页&#xff0c;必须得有用户手动触发&#xff0c;用代码自动打开是会失效的。 也就是说&#xff0c;window.open必须…

VS配置QGIS二次开发(VS2019+QT5+QGIS3.14)

1.1 QGIS软件下载 QGIS软件是基于QT5开发的GIS系统&#xff0c;其易用性及其高效性使其已经成为当下极具竞争力的GIS系统&#xff0c;其占有率足以与Esri的ArcGIS等主流GIS软件分庭抗礼。QGIS软件因其开源特性也使得其广受广大GIS开发者的青睐。 在本次《地理信息系统设计与开…

风趣图解LLMs RAG的15种设计模式-第二课

全部用的是Midjourney连续性一致性绘图画成

前端报文加密

前端加密功能 前端提供简单的AES对称加密算法&#xff0c;注意key 和后端网关配置相同&#xff0c;这里打包混淆后&#xff0c;相对安全。 &#xff08;lun-ui\src\store\modules\user.js、base-gateway-dev.yml&#xff09; 后端解密功能 使用hutool提供的工具类进行解密pub…

鸿蒙开发基础知识 第二篇【页面布局】

鸿蒙开发基础知识 第二篇 1. 两端对齐 demo 2.交叉轴对齐方式 demo 3.列表项布局 demo 4.自适应伸缩布局 demo 自行练习 5.弹性布局 flex 换行布局 demo 案例 更多鸿蒙技能知识与案例 我已经整理到下面了 ↓↓↓ 快去看那看吧&#xff01; 点击下方↓↓↓↓↓↓↓…

【C++】类和对象(三)再探构造函数|static成员函数|友元函数|内部类|匿名对象|对象拷贝时的编译优化

欢迎来到HarperLee的学习笔记&#xff01; 一、再探构造函数 初始化列表&#xff1a;构造函数初始化的第二种方式&#xff08;第一种是使用函数体内赋值&#xff09;。使用方式&#xff1a;以一个冒号:开始&#xff0c;用逗号,分隔数据成员列表&#xff0c;每个成员变量后面跟…

Linux 命令行/bash脚本 批量创建文件

目录 一. 命令行1.1 需求1.2 代码 二. bash脚本2.1 需求2.2 前置知识2.3 无进度条版本2.3.1 知识点2.3.2 代码2.3.3 效果 2.4 有进度条版本2.4.1 代码2.4.2 效果 一. 命令行 1.1 需求 在当目录下生成指定年份的文件&#xff0c;要求从生成1月到12月&#xff0c;每个月份的文件…

如何使用 update-alternatives 切换软件版本 ?

Debian 中的 update-alternatives 命令是系统上多版本软件管理的利器。它允许您轻松地在同一程序的不同版本之间切换。假设您在单个系统上安装了多个 Java 版本或 Python 版本&#xff0c;在 update-alternatives 的帮助下&#xff0c;您可以将程序的任何版本设置为系统的默认版…

SAP MM模块与FI模块集成之科目配置

1. 定义评估范围 OMWD 评估范围设置在工厂层&#xff0c;那么系统自动建立和工厂具有相同ID的评估范围 IMG&#xff1a;物料管理>评估和科目设置>科目确定>无向导的科目确定>将评估范围群集分组 提示&#xff1a;评估层级——评估分组代码——评估范围。 2. OBYC…

【Unity案例】搭建射击系统与UI

上期将基础的移动系统搭建完毕后就可以开始搭建更加复杂的系统部分了 前排提示&#xff0c;由于一开始仅思考如何完成操作相关功能&#xff0c;以至于到后面重构稍微有些困难&#xff0c;继续写下去恐成屎山&#xff0c;故在搭完射击和武器UI后不再继续泛化到敌人和敌人状态机…

SF-HCI-SAP问题收集18:员工上级经理无效

导读 INTRODUCTION CPI集成&#xff1a;这几天一直在处理SuccessFactors与SAP集成的问题&#xff0c;因为没有给deubg权限&#xff0c;排查比较困难&#xff0c;这次讲解的案例就是一个比较难发现的案例&#xff0c;在同步员工主数据的时候提示&#xff0c;经历在某个区间无效…

前端原生Js批量修改页面元素属性的2个方法

前言 嘿&#xff0c;朋友们&#xff01;今天咱们来聊聊一个前端原生 JS 的小技巧。 今天在做一个 RPA 机器人时&#xff0c;碰到一个业务需求&#xff0c;网页上有两个日期控件元素&#xff0c;它们的输入框有 readonly 属性&#xff0c;只能通过选择的方式来输入日期&#x…

数据库和MySQL

ER图 实体&#xff08;矩形&#xff09;&#xff1a;通常是现实世界的业务对象&#xff0c;当然使用一些逻辑对象也可以。 属性&#xff08;椭圆&#xff09;&#xff1a;实体拥有的属性。 联系&#xff08;菱形&#xff09;&#xff1a;实体与实体之间的关系。 函数依赖 函数依…

idea 编译断点运行 tomcat 10.1.28 源码

idea 编译运行 tomcat 10.1.28 源码 1. 所需资源 tomcat 10.1.28 zulu JDK 22 maven idea (支持 JDK 22) 2. Idea 导入项目 10.1.28.tar.gz 解压到指定文件夹 如 ~\tomcat-source\tomcat-10.1.28 这里等待一段时间&#xff0c;生成 ~\tomcat-source\tomcat-10.1.28\.idea 文…

双向链表

双向链表是一种基本的数据结构&#xff0c;它与单向链表的主要区别在于节点的连接方式。下面我将分别描述双向链表的特点以及它与单向链表的区别。 双向链表的特点&#xff1a; 节点结构&#xff1a;在双向链表中&#xff0c;每个节点包含三个部分&#xff1a;数据域、指向前一…

WordPress自适应美图网站整站打包源码

直接服务器整站源码数据库打包了&#xff0c;恢复一下就可以直接投入使用。保证好用易用&#xff0c;无需独立服务器就可以使用。 强调一下&#xff0c;我这个和其他地方的不一样、不一样、不一样。具体的看下面的说明。 现在网络上同样的资源包都是用的加密带后门的主题&…

[C++11#47] (四) function包装器 | bind 函数包装器 | 结合使用

目录 一. function包装器 1. 在题解上 2.bind 绑定 1.调整参数顺序 2.对类中函数的包装方法 一. function包装器 function包装器也叫作适配器。C中的function本质是一个类模板&#xff0c;也是一个包装器。 那么我们来看看&#xff0c;我们为什么需要function呢&#xff1…

[数据集][目标检测]机油泄漏检测数据集VOC+YOLO格式43张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;43 标注数量(xml文件个数)&#xff1a;43 标注数量(txt文件个数)&#xff1a;43 标注类别数…