C++ 实现BSTree

news2024/9/30 5:40:45

目录

BSTree.h

框架

insert

insertR

find

findR

erase

eraseR

InOrder

拷贝构造

赋值重载 


BSTree.h

框架

template<class K>
struct BSTreeNode
{
	BSTreeNode<K>* _left;
	BSTreeNode<K>* _right;
	K _key;

	BSTreeNode(const K& key)
		:_left(nullptr)
		,_right(nullptr)
		,_key(key)
	{}
};
template<class K>
class BSTree
{
	typedef BSTreeNode<K> node;
public:
private:
	node* _root = nullptr;
};

insert

1.在空位置插入,所以就要记录父节点

2.第一次插入时候要判断(_root == nullptr) 因为第一个节点没有父亲,我通过缺省参数初始化_root

3. node* parent = cur; parent初始化什么都可以,分析:与_root相同,则return,不同就延续cur

4.进行链接新节点,上面的while只是找到新节点在的父亲,parent与new node之前只是父子关系,左右孩子还关系需要比较_key的值

5.返回的值是bool,因为BSTree的结构和插入顺序有关

bool insert(const K& key)
{
	if (_root == nullptr)
	{
		_root = new node(key);
		return true;
	}
	node* cur = _root;
	node* parent = cur;
	while (cur)
	{
		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 = new node(key);
	else
		parent->_left = new node(key);
	return true;
}

insertR

通过循环来插入

1.通过 & 就可以直接与父节点链接上,分析:上一个栈帧里root的指向,当前的root的地址,执行到nullptr时候,root所存放的值就是new 出来节点的地址,这样就链接上了

2.仍然是在nullptr的位置插入、

3.对于返回值(函数出口)来说,两个地方:new出来了就是true,相等就是false,其他递归均是过程

4. _insertR(root->_right, key);仔细想了想,这个地方也是返回,不然最后返回的时候没有返回值啊,但是也不报错,也没有警告(vs编译器会自动处理函数的返回值 ,不是像oj上那样 每个返回路径都需要严格加return)但还是加上有味道(因为这是不同的栈帧,不是循环)

bool insertR(const K& key)
{
	return _insertR(_root, key);
}
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;
	}
}

find

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

findR

1.返回的是能否找到,即bool

2.函数出口nullptr 返回false,相等返回 true

3.&可以有也可以没有,其一:这里的值拷贝只是一个指针,其二:不需要有链接关系

bool findR(const K& key)
{
	return _findR(_root, key);
}
bool _findR(node* root, const K& key)//不起到链接作用,但引用也是可以的
{
	if (root == nullptr) return false;
	if (root->_key < key)
	{
		return _findR(root->_right, key);
	}
	else if (root->_key > key)
	{
		return _findR(root->_left, key);
	}
	else
	{
		return true;
	}
}

erase

分析:

1.删3,3的左为空,就需要将3的右给5的左(叶子节点也算在在里面)

2.删18,18的右为空,就需要将18的左给15的右(叶子节点也算在里面)

3.删5,那么就需要有值来代替5,符合条件的是左树的最大节点,或右树的最小节点,随之转换删除的是4或7,而不是5了

4.在右边这种情况,当然也可以选择,与9交换,然后删9,但是直接将_root转换为5更舒服

代码注意事项

1.对于1和2两种删除的情况,因他们可以有儿子,所以需要借助父亲,链接上他们的儿子

2.如果只有一个节点,在清除语句的第一个if,会将_root置空

3.找到的待删除的值,与父亲只有链接关系,链接到左还是右,还需要通过指针进一步判断

4.pmaxleft需要初始化成当cur(不能为nullptr),因为maxleft = pmaxleft -> _left,如果,maxleft没有右树,pmaxleft为空,那么maxleft就找不到父亲,在解引用的时候就会报错,这种情况也正好是pmaxleft->_left == maxleft,其他情况都是右树(也就是while进去了)

5.当左右都不为空的时候 del 需要改成maxleft,因为只是交换了值,cur还在原来位置,不然下面的节点就全丢了

bool erase(const K& key)
{
	node* cur = _root;
	node* parent = cur;
	while (cur)
	{
		if (cur == nullptr) return false;
		if (cur->_key < key)
		{
			parent = cur;
			cur = cur->_right;
		}
		else if (cur->_key > key)
		{
			parent = cur;
			cur = cur->_left;
		}
		else
		{
			break;   //等于准备删除
		}
	}
	if (cur == nullptr) return false;// 空就是没找到
	node* del = cur;
	if (cur->_left == nullptr)
	{
		if (cur == _root)
		{
			_root = cur->_right;
		}
		if (parent->_left == cur)
		{
			parent->_left = cur->_right;
		}
		else
		{
			parent->_right = cur->_right;
		}
	}
	else if (cur->_right == nullptr)
	{
		if (cur == _root)
		{
			_root = cur->_left;
		}
		if (parent->_left == cur)
		{
			parent->_left = cur->_left;//这个地方  ;  没写,报编译器内部错误
		}
		else
		{
			parent->_right = cur->_left;
		}
	}
	else
	{
		node* pmaxleft = cur;
		node* maxleft = cur->_left;
		while (maxleft->_left)
		{
			pmaxleft = maxleft;
			maxleft = maxleft->_right;
		}
		cur->_key = maxleft->_key;
		if (pmaxleft->_left == maxleft)
		{
			pmaxleft->_left = maxleft->_right;
		}
		else
		{
			pmaxleft->_right = maxleft->_right;
		}
		del = maxleft;//因为只是换的节点的值
	}
	delete del;
	return true;
}

eraseR

1.通过循环解决,删除操作本身就需要链接,&很合适

2.如果为nullptr就是没有对应的值,返回false

3.仔细分析下,是怎么删3的,和怎么链接的

这是我第一次画的,其实这个root写在这个位置,并不是很好

应该是这样:在引用传参的时候传的就是5节点里_left,再次进入一个栈帧root->_right就是节点4的地址(需要沉淀一下,从调试的结果是这样的)对于&root自然是在节点10里,因为10->_left存放的是5节点的地址

bool eraseR(const K& key)
{
	return _eraseR(_root, key);
}
bool _eraseR(node*& root, const K& key)
{
	if (root == nullptr) return false;
	if (root->_key < key)//找的过程
	{
		_eraseR(root->_right, key);
	}
	else if (root->_key > key)
	{
		_eraseR(root->_left, key);
	}
	else//找到
	{
		node* del = root;
		if (root->_left == nullptr)
		{
			root = root->_right;//赋值重载,就是要只拷贝
		}
		else if (root->_right == nullptr)
		{
			root = root->_left;
		}
		else
		{
			node* pminright = root;
			node* minright = root->_right;
			while (minright->_left)
			{
				pminright = minright;
				minright = minright->_left;
			}
			swap(minright->_key, root->_key);
			return _eraseR(root->_right, key);
		}
		delete del;
		return true;
	}
}

InOrder

中序遍历

void InOrder()
{
	_InOrder(_root);
	cout << endl;
}
void _InOrder(node* root)
{
	if (root == nullptr) return;

	_InOrder(root->_left);
	cout << root->_key << " ";
	_InOrder(root->_right);
}

拷贝构造

copy例不能把tmp换成_root,不然每一次递归_root都会被覆盖,链接不上,而tmp每次都是一个新的

问题:t是const,那么t._root也是const,为什么copy里的t不需要const修饰

node*可以接收node*const,而且下面的copy里的const怎么加都不影响

比如:node* test = t._root;这是被允许的,这只是一个赋值;node*& test = t._root;这样就不行,关键因为我只是用了他的值,它加了const只是不能修改它,但是可以去他的值

BSTree(const BSTree<K>& t)
{
	_root = copy(t._root);
}
node* copy(node* t)
{
	if (t == nullptr) return nullptr;
	node* tmp = new node(t->_key);
	tmp->_left = copy(t->_left);
	tmp->_right = copy(t->_right);
	return tmp;
}

赋值重载 

我遇到的问题:这个改正了我之前错误的理解:我之前认为这里的t是不会被析构的,因为他是在堆上的,在栈上出了作用域会自己弹掉;但是t(t是拷贝构造出来的)出了这个函数作用域就被应该销毁(这是我在调试的时候看到的),而且堆不就是要被析构吗,

问题就在swap上:swap没有吧t._root换掉就会被析构掉,导致_root也没了,所以要把t._root置空

//①用不用std里的会用自己写的,就会无限递归
//②void swap(node*& t)和void swap(node* t)会调用不明确
void swap(node*& t)// 因为这里的t会被析构掉,它已经出了它的函数作用域
{//		这也是为什么刚进入析构_root就是nullptr,所以这个t必须是&传参,要换掉这个t
	//swap(_root, t);
	std::swap(_root, t);
}
void swap(node** t)//和swap(&t._root)对应
{
	std::swap(_root, *t);
}
//void swap(node* t)//和swap(t._root)对应,传拷贝的也不是不可以,出去之后要把t._root改成nullptr☆
//{
//	std::swap(_root, t);
//}
//BSTree<K>& operator=(const BSTree<K> t)//swap掉不了
BSTree<K>& operator=(BSTree<K> t)//
{
	//swap(&t._root);//就是要改掉原来的t ->void swap(node** t)
	swap(t._root);
	t._root = nullptr;
	//swap(_root, t._root);//可以
	return *this;
}

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

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

相关文章

2024智能EDM邮件营销系统使用攻略

在数字化营销领域&#xff0c;智能EDM&#xff08;Electronic Direct Mail&#xff09;邮件营销作为一种高效、精准的推广方式&#xff0c;正日益受到企业的高度重视。而要实现这一策略的成功落地&#xff0c;一个高可靠性和高稳定性的专业邮件发送平台则是不可或缺的关键环节。…

Linux V4L2 应用编程

V4L2&#xff1a;Video4Linux2&#xff0c;是 Linux 内核中的一个框架&#xff0c;提供了一套用于视频设备驱动程序开发的 API。它是一个开放的、通用的、模块化的视频设备驱动程序框架&#xff0c;允许 Linux 操作系统和应用程序与各种视频设备&#xff08;如摄像头、视频采集…

Less-1(sqlmap手工注入攻击)--sqli

第一步&#xff1a;判断他是什么sql注入&#xff1f; 1 报错 1 and 12 -- 错误结果(--表示注释符) 1 and 11 -- 正确结果 第二步&#xff1a;判断返回字段数 ?id1 order by 3-- 正确显示结果 ?id1 order by 4--当列数为4时开始报错&#xff0c;所以只有三列 注&#xf…

aurora仿真使用等

IP设置 代码 aurora_8b10b aurora_8b10b_inst (/**********************************************************************************///axi_stream tx.s_axi_tx_tdata(s_axi_tx_tdata), // input wire [0 : 31] s_axi_tx_tdata.s_axi_tx_tkeep(s_axi_tx_…

C++|类封装、类的分文件编写练习:设计立方体类、点和圆的关系

文章目录 练习案例1&#xff1a;设计立方体类CPP代码 练习案例2:点和圆的关系CPP代码 代码总结类的分文件编写 练习案例1&#xff1a;设计立方体类 设计立方体类(Cube) 求出立方体的面积和体积 分别用全局函数和成员函数判断两个立方体是否相等。 CPP代码 class Cube { pub…

【数据结构】堆和树详解堆和二叉树的实现堆的top-k问题

主页&#xff1a;醋溜马桶圈-CSDN博客 专栏&#xff1a;数据结构_醋溜马桶圈的博客-CSDN博客 gitee&#xff1a;mnxcc (mnxcc) - Gitee.com 目录 1.树概念及结构 1.1 树的概念 2.2 树的相关概念 1.3 树的表示 1.4 树在实际中的运用 2.二叉树的概念及结构 2.1 二叉树的概念…

手撕算法-长度最小的子数组

描述 分析 滑动窗口。窗口内的和大于等于tatger时&#xff0c;记录此时的长度&#xff0c;并比较是不是最小长度。窗口左边界右移&#xff0c;直到窗口内的和小于tatger。窗口内的和小于tatger时&#xff0c;窗口右边界右移。 代码 class Solution {public int minSubArray…

基于python+vue分类信息服务平台移动端的设计与实现flask-django-php-nodejs

分类信息服务平台是在Android操作系统下的应用平台。为防止出现兼容性及稳定性问题&#xff0c;框架选择的是django&#xff0c;Android与后台服务端之间的数据存储主要通过MySQL。用户在使用应用时产生的数据通过 python等语言传递给数据库。通过此方式促进分类信息服务平台信…

牛客NC108 最大正方形【中等 动态规划 Java,Go,PHP】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/0058c4092cec44c2975e38223f10470e 思路 动态规划: 先初始化第一行和第一列。然后其他单元格依赖自己的上边&#xff0c;左边和左上角参考答案Java import java.util.*;public class Solution {/*** 代码中的类…

【Linux】调试器-gdb的使用说明(调试器的配置,指令说明,调试过程说明)

目录 00.背景 01.安装 02.生成调试信息 03.调试过程 00.背景 在软件开发中&#xff0c;通常会为程序构建两种不同的版本&#xff1a;Debug模式和Release模式。它们之间的区别主要在于优化级别、调试信息、错误检查等方面&#xff1a; 1.Debug 模式&#xff1a; 优化级别低…

阿里云ECS服务器u1通用算力型CPU性能如何?

阿里云服务器u1是通用算力型云服务器&#xff0c;CPU采用2.5 GHz主频的Intel(R) Xeon(R) Platinum处理器&#xff0c;通用算力型u1云服务器不适用于游戏和高频交易等需要极致性能的应用场景及对业务性能一致性有强诉求的应用场景(比如业务HA场景主备机需要性能一致)&#xff0c…

Gremlin查询语言用法示例

Gremlin 的基本用法 Gremlin Query Language 的基本用法主要包括构建图遍历的查询语句&#xff0c;这些语句由一系列的步骤组成&#xff0c;用于从图形中检索数据和修改数据。以下是一些基本用法的示例和解释&#xff1a; 选择所有顶点&#xff1a; 使用g.V()可以选择图中的所…

包叔推荐12代i3-独显组装电脑主机配置清单

去年Intel第十代i5-依然是主流热选机型。 今年&#xff0c;随着i3-的价格优势越来越大&#xff0c;已经成功取代了i5-。 今天包叔推荐几套12代i3-独立显卡组装电脑主机配置。 列表&#xff1a;一组核心显示配置&#xff0c;其余三组均为独立显示配置。 适合主机预算在2000元至3…

Spring Cloud Gateway教程

1 微服务网关概述 Spring Cloud Gateway是在 Spring 生态系统之上构建的API网关服务&#xff0c;旨在为微服务架构应用提供一种简单有效的统一的API路由管理方式。 Spring Cloud Gateway主要功能&#xff1a; 反向代理认证鉴权流量控制熔断日志监控 2 Spring Cloud Gateway三…

搭建一个简单的网络结构(Pytorch实现二分类)

搭建一个简单的网络结构&#xff08;Pytorch实现二分类&#xff09; 搭建一个神经网络并进行训练的话&#xff0c;大致需要分为三步&#xff1a; 第一步是数据的处理&#xff0c;将数据整理成输入网络结构中合适的格式第二步是网络的搭建&#xff0c;包括每层网络的结构和前向…

Neo4j桌面版导入CVS文件

之后会出来一个提示框&#xff0c;而且会跳出相关文件夹&#xff1a; 然后我们将CSV文件放在此目录下&#xff1a; 我们的relation.csv是这样的 参见&#xff1a; NEO4J的基本使用以及桌面版NEO4J Desktop导入CSV文件_neo4j desktop使用-CSDN博客

数学建模体育建模和经济建模国防科大版

目录 6.体育中的数学建模 7.经济学问题中的数学建模 7.1.实物交换模型 7.2.边际效应 7.3.最佳消费选择模型 6.体育中的数学建模 体育科学的研究中&#xff0c;也有大量的数学建模问题&#xff0c;例如&#xff1a;棒球的最佳击球点问题、滑板滑雪赛道的设计、越野自行车比…

基于springboot+vue的旅游推荐系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

全网最强JavaWeb笔记 | 万字长文爆肝JavaWeb开发——Web开发介绍

万字长文爆肝黑马程序员2023最新版JavaWeb教程。这套教程打破常规&#xff0c;不再局限于过时的老套JavaWeb技术&#xff0c;而是与时俱进&#xff0c;运用的都是企业中流行的前沿技术。笔者认真跟着这个教程&#xff0c;再一次认真学习一遍JavaWeb教程&#xff0c;温故而知新&…

利用免费 GPU 部署体验大型语言模型推理框架 vLLM

vLLM简介 vLLM 是一个快速且易于使用的 LLM&#xff08;大型语言模型&#xff09;推理和服务库。 vLLM 之所以快速&#xff0c;是因为&#xff1a; 最先进的服务吞吐量 通过 PagedAttention 高效管理注意力键和值内存 连续批处理传入请求 使用 CUDA/HIP 图快速模型执行 量…