【C++】Binary Search Tree

这篇博客要说的是二叉搜索树,又叫二叉排序树,它或者是一颗空树,或者是具有以下性质的二叉树:

·若它的左子树不为空,那么左子树上所有节点的值都小于根节点的值,不会出现等于的情况

·若它的右子树不为空,那么右子树上所有节点的值都大于根节点的值,不会出现等于的情况

·它的左右子树也分别为二叉搜索树

我们可以由上边的特点推断出二叉树的中序遍历是有序的,比如给一个二叉搜索树

中序遍历就是7 15 17 22 27 30 45 60 75这是有序的。

知道了它的这些特性,那么我们来自己来模拟实现一下:

我们首先需要一个节点的类

然后我们写一个查找函数,就是根据我们上边的规则进行查找

下面我们写一个插入元素的函数,我们在插入的时候,一味的找底为空是不行的,我们需要记录下它的父亲的节点才可以

此时我们就可以创建一个二叉搜索树,那么我按中序遍历试一试,究竟是不是有序的呢?我们就写一个中序遍历的打印函数,我们在调用函数的时候是不需要传值的,而递归打印又需要根节点的值,所以我们封装一层

下面就是我们删除节点的函数,删除的节点有三种情况,没有孩子,有一个孩子和有两个孩子。其中没有孩子和有一个孩子可以一起处理,就是有两个孩子的比较难处理,我们选择用替换法:就是用左子树中最大节点或右子树中最小节点去替换掉我们要删除的值。大家可以想一想为什么,因为我这样替换并不影响搜索二叉树的性质。由于代码比较长,我们放在文章的最后

下面我们来递归实现以下查找和插入函数,跟我们的打印一样,我们要实现递归就要封装一层,我们先来实现简单的插入和查找

注意了:_insertR这里第一个参数我们用的引用,如果不用引用的话,那么root就只是一个拷贝,并不会对实际的二叉树产生影响。下面是删除函数的递归形式

那如果我们想用一棵二叉树生成另一颗二叉树,我们就要写拷贝构造,因为默认生成的是浅拷贝,以及后面一系列的析构和赋值重载

上面就是我们的类的一系列函数下面有两个模型,一个叫key模型,一个叫keyvalue模型,key搜索模型就是快速查找一个值是否存在,比如我们的门禁系统,小区车库都是根据你的信息,去系统中查找这个值是否存在。keyvalue模型是通过一个值去查找另一个值是否存在,比如说:商场车库,通过车牌号去查找你的停车时间,比如手机上的词典软件,高铁站刷票系统,通过你的信息去查找这辆列车有没有你的信息。

下面是所有的代码

template<class K>
struct BSTreeNode {
	BSTreeNode(const K& key)
		:left(nullptr)
		,right(nullptr)
		,_key(key){}
	BSTreeNode(){}
	BSTreeNode* left=nullptr;
	BSTreeNode* right=nullptr;
	K _key=K();
};

template<class K>
class BSTree {
	typedef BSTreeNode<K> Node;
public:
	bool find(const K& key) {
		if (_root == nullptr) {
			return false;
		}
		Node* cur = _root;
		while (cur != nullptr) {
			if (cur->_key < key)cur = cur->right;
			else if (cur->_key > key)cur = cur->left;
			else return true;
		}
		return false;
	}
	bool insert(const K& key) {
		Node* tmp = new Node(key);
		//tmp->_key = key;
		if (_root == nullptr) {
			_root = tmp;
			return true;
		}
		else {
			Node* cur = _root;
			Node* parent = _root;
			while (cur != nullptr) {
				if (cur->_key < key) {
					parent = cur;
					cur = cur->right;
				}
				else if (cur->_key > key) {
					parent = cur;
					cur = cur->left;
				}
				else return false;
			}
			if (parent->_key < key)parent->right = tmp;
			else if (parent->_key > key)parent->left = tmp;
			return true;
		}
	}
	bool erase(const K& key) {
		if (_root == nullptr)return false;
		else {
			Node* cur = _root;
			Node* parent = _root;
			while (cur != nullptr) {
				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 (parent->left == cur) {
								parent->left = cur->right;
							}
							else if (parent->right == cur) {
								parent->right = cur->right;
							}
							else {
								Node* tmp = _root;
								_root = _root->right;
							}
							delete cur;
							return true;
						}
						else if(cur->right==nullptr) {
							if (parent->left == cur) {
								parent->left = cur->left;
							}
							else if (parent->right == cur) {
								parent->right = cur->left;
							}
							else {
								Node* tmp = _root;
								_root = _root->left;
							}
							delete cur;
							return true;
						}
					else {
						Node* leftreemax = cur->left;
						Node* leftreemaxpa = cur;
						while (leftreemax->right != nullptr) {
							leftreemaxpa = leftreemax;
							leftreemax = leftreemax->right;
						}
						cur->_key = leftreemax->_key;
						leftreemaxpa->left = leftreemax->left;
						delete leftreemax;
						return true;
					}
				}
			}
			return false;
		}
	}
								
	

	void PrintBSTree() {
		_PrintBSTree(_root);
		cout << endl;
	}
	bool findR(const K& key) {
		return _findR(_root, key);
	}
	bool insertR(const K& key) {
		return _insertR(_root, key);
	}
	bool eraseR(const K& key) {
		return _eraseR(_root, key);
	}
	~BSTree() {
		Destroy(_root);
	}
	BSTree() = default;//强制生成默认构造
	BSTree(const BSTree<K>& t) {
		_root=copy(t._root);
	}
	BSTree<K>& operator=(BSTree<K> t)
	{
		swap(_root, t._root);
		return *this;
	}
private:
	Node* copy(Node* root) {
		if (root == nullptr)return nullptr;
		Node* newnode = new Node(root->_key);
		newnode->left = copy(root->left);
		newnode->right = copy(root->right);
		return newnode;
	}

	void Destroy(Node* root) {
		if (root == nullptr)return;
		Destroy(root->left);
		Destroy(root->right);
		delete root;
	}

	bool _eraseR(Node*& root, const K& key) {
		if (root == nullptr)return false;
		if (root->_key < key) {
			return _eraseR(root->right, key);
		}
		else if (root->_key > key) {
			return _eraseR(root->left, key);
		}
		else {
			if (root->left == nullptr) {
				Node* tmp = root;
				root = root->right;
				delete tmp;
				return true;
			}
			else if (root->right == nullptr) {
				Node* tmp = root;
				root = root->left;
				delete tmp;
				return true;
			}
			else {
				Node* leftreemaxpa = root;
				Node* leftreemax = root->left;
				while (leftreemax->right != nullptr) {
					leftreemaxpa = leftreemax;
					leftreemax = leftreemax->right;
					}
					swap(root->_key, leftreemax->_key);
					return _eraseR(root->left, key);
					/*root->_key = leftreemax->_key;
					if (leftreemax == leftreemaxpa->left)leftreemaxpa->left = leftreemax->left;
					else leftreemaxpa->right = leftreemax->left;
					delete leftreemax;
					return true;*/
				}
			}
		}
	

	bool _insertR(Node*& root, const K& key) {
		if (root == nullptr) {
			root = new Node(key);
			return true;
		}
		if (root->_key < key) {
			return _insertR(root->right, key);
		}
		else if (root->_key > key) {
			return _insertR(root->left, key);
		}
		else return false;
	}

	bool _findR(Node* root, const K& key) {
		if (root == nullptr)return false;
		if (root->_key == key)return true;
		else if (root->_key < key)return _findR(root->right, key);
		else return _findR(root->left, key);
	}

	void _PrintBSTree(Node* root) {
		if (root == nullptr) return;
		_PrintBSTree(root->left);
		cout << root->_key << ' ';
		_PrintBSTree(root->right);
	}
	Node* _root=nullptr;
};

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

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

相关文章

数据结构——快速排序的三种方法和非递归实现快速排序

数据结构——快速排序的三种方法和非递归实现快速排序&#xff08;升序&#xff09; 快速排序的单趟排序hoare法挖坑法前后指针法 快速排序的实现key基准值的选取快速排序代码快速排序的优化 快速排序&#xff08;非递归&#xff09; 快速排序的单趟排序 hoare法 思路:从给定…

C++第十三弹---内存管理(下)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】 目录 1、operator new与operator delete函数 1.1、operator new与operator delete函数 2、new和delete的实现原理 2.1、内置类型 2.2、自定义类型 …

基于模糊控制算法的倒立摆控制系统simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 对倒立摆模型进行模糊控制器simulink建模&#xff0c;利用倒立摆的摆角角度与小车的位置来控制小车的推力&#xff0c;控制了倒立摆的摆角问题&#xff0c;使得小车最终停在稳…

Redis面试题-缓存雪崩、缓存穿透、缓存击穿问题

1 穿透: 两边都不存在&#xff08;皇帝的新装&#xff09; &#xff08;黑名单&#xff09; &#xff08;布隆过滤器&#xff09; 2 击穿&#xff1a;一个热点的key失效了&#xff0c;这时大量的并发请求直接到达数据库. &#xff08;提前预热&#xff09; 3 雪崩&#xff1a…

LeetCode 27 移除元素

给你一个数组 nums 和一个值 val&#xff0c;你需要 原地 移除所有数值等于 val 的元素&#xff0c;并返回移除后数组的新长度。 不要使用额外的数组空间&#xff0c;你必须仅使用 O(1) 额外空间并 原地 修改输入数组。 元素的顺序可以改变。你不需要考虑数组中超出新长度后面…

【Emgu CV教程】10.9、轮廓的纵横比、面积比、坚硬性、等效直径、方向、掩码及像素点、最值点和它的位置、平均颜色、极值点

文章目录 一、轮廓的纵横比(Aspect Ratio)二、轮廓的面积比(Extent)三、轮廓的坚硬性(Solidity)四、轮廓的等效直径(Equivalent Diameter)五、轮廓的方向(Orientation)六、轮廓的掩码以及像素点(Mask and Pixel Points)1.原始素材2.代码3.运行结果 七、轮廓的值点和它的位置八、…

Spring实战:采用Spring配置文件管理Bean

文章目录 一、Spring框架概述二、实战&#xff1a;采用Spring配置文件管理Bean&#xff08;一&#xff09;创建Jakarta EE项目&#xff08;二&#xff09;添加Spring依赖&#xff08;三&#xff09;创建杀龙任务类&#xff08;四&#xff09;创建勇敢骑士类&#xff08;五&…

基于springboot实现校园周边美食探索及分享平台系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现园周边美食探索及分享平台系统演示 摘要 美食一直是与人们日常生活息息相关的产业。传统的电话订餐或者到店消费已经不能适应市场发展的需求。随着网络的迅速崛起&#xff0c;互联网日益成为提供信息的最佳俱渠道和逐步走向传统的流通领域&#xff0c;传统的…

uni-app(使用阿里图标)

1.注册阿里矢量图标库 注册阿里图标库账号并登录&#xff0c;https://www.iconfont.cn/ 2.加入购物车 搜索适合自己的图标&#xff0c;加入购物车&#xff0c;如下图&#xff1a; 3.加入项目 我的->资源管理->我的项目->创建项目&#xff0c;然后返回购物车&#…

SpringCloud学习笔记二:服务间调用

微服务中&#xff0c;很多服务系统都在独立的进程中运行&#xff0c;通过各个服务系统之间的协作来实现一个大项目的所有业务功能。服务系统间 使用多种跨进程的方式进行通信协作&#xff0c;而RESTful风格的网络请求是最为常见的交互方式之一。 spring cloud提供的方式&#…

工厂数字化看板是什么?部署工厂数字化看板有什么作用?

随着工业4.0时代的来临&#xff0c;数字化转型已成为制造业发展的必然趋势。在这个背景下&#xff0c;工厂数字化看板作为一种高效的信息展示与管理工具&#xff0c;正逐渐受到越来越多企业的青睐。那么&#xff0c;什么是工厂数字化看板&#xff1f;部署工厂数字化看板又有哪些…

真没想到,SQL注入漏洞的这么大,竟然导致1400万名俄罗斯大学毕业生信息泄露

不知道各位面试时&#xff0c;有没有相关的面试官有没有问到这样的问题&#xff0c;什么是sql注入&#xff0c;sql注入的危害是什么&#xff0c;mybatis的#与$的区别是什么等等&#xff0c;我想很多人都知道使用mybatis的#去规避sql注入&#xff0c;但是很多人不知道其原理&…

备份SQLserver数据库到本地位置

怎么选择合适的数据库备份方案&#xff1f; 有人可能会说SSMS&#xff0c;确实&#xff0c;SSMS作为一个微软官方提供的SQLserver数据库管理工具&#xff0c;是可以帮助我们完成对数据库的备份还原任务的&#xff0c;但是它也有一些局限性&#xff0c;比如不能进行批量化的备份…

LLM应用:Prompt flow vs LangChain

背景 Prompt flow和LangChain都是LLM时代&#xff0c;为高效地构建LLM应用而生。 Prompt flow是Microsoft开源的&#xff0c;其诞生时&#xff0c;LangChain已经很有名气了。 所以作为后生的Prompt flow会为我们带来哪些新的东西呢&#xff1f; ​​​​​​​ Prompt flo…

JTW——01,简述、对比

简述、对比 一、jwt跟token的区别二、什么是jwt三、jwt能做什么四、传统的session认证五、Jwt认证 一、jwt跟token的区别 https://blog.csdn.net/wangxinxinsj/article/details/132746876 二、什么是jwt 三、jwt能做什么 四、传统的session认证 五、Jwt认证

Docker搭建LNMP环境实战(06):Docker及Docker-compose常用命令

Docker搭建LNMP环境实战&#xff08;06&#xff09;&#xff1a;Docker及Docker-compose常用命令 此处列举了docker及docker-compose的常用命令&#xff0c;一方面可以做个了解&#xff0c;另一方面可以在需要的时候进行查阅。不一定要强行记忆&#xff0c;用多了就熟悉了。 1、…

ETL工具-nifi干货系列 第五讲 处理器GenerateFlowFile

1、今天我们一起来学习处理器GenerateFlowFile。这个处理器创建带有随机数据或自定义内容的 FlowFiles。GenerateFlowFile 对于负载测试、配置和模拟非常有用。从工具栏拖动处理器到画布&#xff0c;然后选择GenerateFlowFile即可。 2、点击add按钮或者双击 GenerateFlowFile可…

大型矿业集团安全知识竞赛主持词

男&#xff1a;尊敬的各位领导&#xff0c;员工同志们&#xff1a; 合&#xff1a;大家好&#xff01; 男&#xff1b;首先让我们以热烈的掌声对公司领导亲临比赛现场指导观看表示欢迎&#xff01; 男&#xff1b;继成功开展了荣辱观专题讲座、好矿嫂女红艺术展、安全谜语竞猜…

CCF-CSP认证考试 202212-3 JPEG 解码 100分题解

更多 CSP 认证考试题目题解可以前往&#xff1a;CSP-CCF 认证考试真题题解 原题链接&#xff1a; 202212-3 JPEG 解码 时间限制&#xff1a; 1.0s 内存限制&#xff1a; 512.0MB 问题背景 四年一度的世界杯即将画上尾声。在本次的世界杯比赛中&#xff0c;视频助理裁判&…

MySQL为什么会选错索引

在平时不知道一有没有遇到过这种情况&#xff0c;我明明创建了索引&#xff0c;但是MySQL为何不用索引呢&#xff1f;为何要进行全索引扫描呢&#xff1f; 一、对索引进行函数操作 假设现在维护了一个交易系统&#xff0c;其中交易记录表 tradelog 包含交易流水号(tradeid)、交…