二叉搜索树/二叉排序树(代码实现)(c++)

news2024/11/25 8:15:11

BSTree

  • 二叉排序树概念
    • 代码部分
  • BSTree框架
    • 查找操作
    • 插入操作
    • 删除操作**
    • 默认成员函数
    • 完整代码
  • BSTree性能分析

二叉排序树概念

二叉搜索树又称二叉排序树(BSTree, Binary Search Tree),它或者是一颗空树,或者是具有以下性质的二叉树:

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

在这里插入图片描述

代码部分

BSTree框架

//创建二叉树结点
template<class K>
struct BSTNode
{
	struct BSTNode<K>* _left;
	struct BSTNode<K>* _right;
	K _key;

	//构造函数
	BSTNode(const K& key)
		:_key(key)
		,_left(nullptr)
		,_right(nullptr)
	{}
};


template<class K>
class BSTree
{
	typedef BSTNode<K> Node;
	//.....
	private:
	Node* _root = nullptr;
};

查找操作

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

非递归:

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

递归:利用树的特性。

//暴露接口的函数
	bool EFind(const K& key)
	{
		return _EFind(_root, key);
	}
	
	bool _EFind(Node* root, const K& key)
	{
		if (root == nullptr)
			return false;
		if (root->_key > key)  
		{
			return _EFind(root->_left, key);
		}
		else if (root->_key < key)
		{
			return _EFind(root->_right, key);
		}
		else
		{
			return true;
		}
	}

插入操作

a. 树为空,直接新增新节点,赋值给root指针
b. 树不空,按二叉搜索树性质查找插入位置,插入新节点

非递归:

	//插入
	bool Insert(const K& key)
	{
		//空
		if (nullptr == _root)
		{
			_root = new Node(key);
			return true;
		}
		//不空
		Node* cur = _root;
		Node* parent = nullptr;
		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->_left = new Node(key);
		}
		else
		{
			//右边插入
			parent->_right = new Node(key);
		}

		return true;
	}

递归版本:利用指针的引用!!

	bool EInsert(const K& key)
	{
		return _EInsert(_root, key);
	}

	//指针的引用!!!
	bool _EInsert(Node*& root, const K& key)
	{
		if (root == nullptr)
		{
			root = new Node(key);
			return true;
		}
		if (root->_key > key)
			return _EInsert(root->_left, key);
		else if (root->_key < key)
			return _EInsert(root->_right, key);
		else  //相等
		{
			return false;
		}
	}

删除操作**

首先查找元素是否在二叉搜索树中,若不存在,则返回,否则要删除的结点可能分下面四种情况:
a. 要删除的结点无孩子结点
b.要删除的结点只有左孩子
c.要删除的结点只有右孩子
d.要删除的结点有左、右孩子结点
表面上有四种,但是实际上情况a可以与b、c情况合并,因此真正删除的情况如下:

  • 情况b:删除该节点且使被删除结点的双亲结点指向被删除结点的左孩子结点
    在这里插入图片描述

  • 情况c:删除该结点,并且使被删除结点的双亲结点指向被删除结点的右孩子结点。
    在这里插入图片描述

  • 情况d:找到被删除结点中序遍历的前一个(或者后一个)结点,将他们俩的值交换,交换后的那个结点。就变成了b\c两种情况。

在这里插入图片描述

非递归版本:

	//删除
	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//找到了
			{
				//左右孩子都没有,就是叶子,其实可以和b、c两种情况合并
				if (cur->_left == nullptr && cur->_right == nullptr)
				{
					if (cur == _root)
					{
						_root = nullptr;
					}
					else if (parent->_left == cur)
					{
						parent->_left = nullptr;
					}
					else if (parent->_right == cur)
					{
						parent->_right = nullptr;
					}
					delete cur;
				}
				else  //不是叶子
				{
					//只有左为空
					if (cur->_left == nullptr)
					{
						if (parent == nullptr)   //防止最开始找到parent为空
						{
							_root = cur->_right;
						}
						else
						{
							if (parent->_left == cur)
							{
								parent->_left = cur->_right;
							}
							else if (parent->_right == cur)
							{
								parent->_right = cur->_right;
							}
						}

						delete cur;
					}
					//只有右为空
					else if (cur->_right == nullptr)
					{
						if (parent == nullptr)  //防止最开始找到parent为空
						{
							_root = cur->_left;
						}
						else
						{
							if (parent->_left == cur)
							{
								parent->_left = cur->_left;
							}
							else if (parent->_right == cur)
							{
								parent->_right = cur->_left;
							}
						}

						delete cur;
					}
					//左右都不为空, 找右子树的最左结点代替
					else
					{
						parent = cur;
						Node* p = cur->_right;
						while (p->_left)  //找紧挨着的结点作替换
						{
							parent = p;
							p = p->_left;
						}
						cur->_key = p->_key;
						if (parent->_left == p)
							parent->_left = p->_right;
						else if (parent->_right == p)
							parent->_right = p->_right;
						delete p;
					}
				}
				return true;
			}
		}
		return false;
	}

递归版本:该思路很巧妙,注意学习

	bool EErase(const K& key)
	{
		return _Erase(_root, key);
	}

	bool _Erase(Node*& root, const K& key)
	{
		//先找key
		if (root == nullptr)
		{
			return false;
		}

		if (root->_key > key)
		{
			return _Erase(root->_left, key);
		}
		else if (root->_key < key)
		{
			return _Erase(root->_right, key);
		}
		else
		{
			//找到了
			Node* del = root;
			if (root->_left == nullptr)
			{
				root = root->_right;
			}
			else if (root->_right == nullptr)
			{
				root = root->_left;
			}
			else
			{
				// 左右都不为空,找左子树最右边的
				Node* minNode = root->_left;
				while (minNode->_right)
				{
					minNode = minNode->_right;
				}
				swap(root->_key, minNode->_key);
				//交换完删再删除结点,精华部分
				return _Erase(root->_left, key);
			}
			delete del;
			return true;
		}
	}

默认成员函数

//构造函数
	//BSTree():_root(nullptr){}
	BSTree() = default;  //强制生成默认构造函数

//拷贝构造
	BSTree(const BSTree<K>& t)
	{
		_root = Copy(t._root);
	}
//前序遍历拷贝构造!!
	Node* Copy(Node* root)
	{
		if (root == nullptr)
		{
			return nullptr;
		}
		Node *newRoot = new Node(root->_key);
		newRoot->_left = Copy(root->_left);
		newRoot->_right = Copy(root->_right);
		return newRoot;
	}


//赋值运算符重载
	BSTree<K>& operator=(BSTree<K> t)
	{
		swap(_root, t._root);
		return *this;
	}
	
//析构
	~BSTree()
	{
		Destory(_root);
	}
	void Destory(Node*& root)
	{
		//后续递归
		if (root == nullptr)
		{
			return;
		}
		Destory(root->_left);
		Destory(root->_right);
		delete root;
		root = nullptr;
	}

完整代码

//BSTree.h



#pragma once
//创建二叉树结点
template<class K>
struct BSTNode
{
	struct BSTNode<K>* _left;
	struct BSTNode<K>* _right;
	K _key;

	//构造函数
	BSTNode(const K& key)
		:_key(key)
		,_left(nullptr)
		,_right(nullptr)
	{}
};

template<class K>
class BSTree
{
	typedef BSTNode<K> Node;
public:

	//BSTree():_root(nullptr){}

	BSTree() = default;  //强制生成默认构造函数


	//拷贝构造
	BSTree(const BSTree<K>& t)
	{
		_root = Copy(t._root);
	}

	//赋值运算符重载
	BSTree<K>& operator=(BSTree<K> t)
	{
		swap(_root, t._root);
		return *this;
	}

	~BSTree()
	{
		Destory(_root);
	}

	//插入
	bool Insert(const K& key)
	{
		//空
		if (nullptr == _root)
		{
			_root = new Node(key);
			return true;
		}
		//不空
		Node* cur = _root;
		Node* parent = nullptr;
		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->_left = new Node(key);
		}
		else
		{
			//右边插入
			parent->_right = new Node(key);
		}

		return true;
	}
	
	bool EInsert(const K& key)
	{
		return _EInsert(_root, key);
	}



	bool EFind(const K& key)
	{
		return _EFind(_root, key);
	}



	bool EErase(const K& key)
	{
		return _Erase(_root, key);
	}


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

	}

	//删除
	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//找到了
			{
				//叶子
				if (cur->_left == nullptr && cur->_right == nullptr)
				{
					if (cur == _root)
					{
						_root = nullptr;
					}
					else if (parent->_left == cur)
					{
						parent->_left = nullptr;
					}
					else if (parent->_right == cur)
					{
						parent->_right = nullptr;
					}
					delete cur;
				}
				else  //不是叶子
				{
					//只有左为空
					if (cur->_left == nullptr)
					{
						if (parent == nullptr)   //防止最开始找到parent为空
						{
							_root = cur->_right;
						}
						else
						{
							if (parent->_left == cur)
							{
								parent->_left = cur->_right;
							}
							else if (parent->_right == cur)
							{
								parent->_right = cur->_right;
							}
						}

						delete cur;
					}
					//只有右为空
					else if (cur->_right == nullptr)
					{
						if (parent == nullptr)  //防止最开始找到parent为空
						{
							_root = cur->_left;
						}
						else
						{
							if (parent->_left == cur)
							{
								parent->_left = cur->_left;
							}
							else if (parent->_right == cur)
							{
								parent->_right = cur->_left;
							}
						}

						delete cur;
					}
					//左右都不为空, 找右子树的最左结点代替
					else
					{
						parent = cur;
						Node* p = cur->_right;
						while (p->_left)
						{
							parent = p;
							p = p->_left;
						}
						cur->_key = p->_key;
						if (parent->_left == p)
							parent->_left = p->_right;
						else if (parent->_right == p)
							parent->_right = p->_right;
						delete p;
					}

					


				}
				return true;

				
			}

		}
		return false;
		

	}
	void InOrder()
	{
		_InOrder(_root);
	}


protected:

	void Destory(Node*& root)
	{
		//后续递归
		if (root == nullptr)
		{
			return;
		}
		Destory(root->_left);
		Destory(root->_right);
		delete root;
		root = nullptr;
	}


	Node* Copy(Node* root)
	{
		if (root == nullptr)
		{
			return nullptr;
		}
		Node *newRoot = new Node(root->_key);
		newRoot->_left = Copy(root->_left);
		newRoot->_right = Copy(root->_right);
		return newRoot;
	}

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

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


	//指针的引用!!!
	bool _EInsert(Node*& root, const K& key)
	{
		if (root == nullptr)
		{
			root = new Node(key);
			return true;
		}
		if (root->_key > key)
			return _EInsert(root->_left, key);
		else if (root->_key < key)
			return _EInsert(root->_right, key);
		else  //相等
		{
			return false;
		}
	}



	bool _Erase(Node*& root, const K& key)
	{
		//先找key
		if (root == nullptr)
		{
			return false;
		}

		if (root->_key > key)
		{
			return _Erase(root->_left, key);
		}
		else if (root->_key < key)
		{
			return _Erase(root->_right, key);
		}
		else
		{
			//找到了
			Node* del = root;
			if (root->_left == nullptr)
			{
				root = root->_right;
			}
			else if (root->_right == nullptr)
			{
				root = root->_left;
			}
			else
			{
				// 左右都不为空,找左子树最右边的
				Node* minNode = root->_left;
				while (minNode->_right)
				{
					minNode = minNode->_right;
				}
				swap(root->_key, minNode->_key);
				//交换完删再删除结点
				return _Erase(root->_left, key);
			}
			delete del;
			return true;
		}
	}
private:
	Node* _root = nullptr;
};

BSTree性能分析

在这里插入图片描述
最好的情况如左图:插入删除效率都是O(logn)
最坏的情况如右图:插入删除效率是O(n),这样就失去了二叉树的优点。

因此后面会介绍AVL树和红黑树!!

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

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

相关文章

IP代理测试:关于Ping测试你需要知道的一切干货

您在访问互联网时是否遇到过持续滞后或花费很长时间等待网站加载的情况&#xff1f;为了避免这种情况&#xff0c;您可以测试 ping 以查看连接速度。如果您使用代理&#xff0c;此 ping 测试还会显示代理服务器的响应速度。 ping 测试是一个很有价值的工具&#xff0c;可以帮助…

武汉灰京文化:技术先锋辐射游戏行业,带来全新体验乐趣无穷!

科技的持续演进&#xff0c;给游戏产业打了强心剂&#xff0c;让这个领域变得前所未有的越来越好玩儿。今天我们将深入探讨如何利用虚拟现实&#xff08;VR&#xff09;和增强现实&#xff08;AR&#xff09;技术&#xff0c;让你玩得开心&#xff0c;玩得尽兴。 想象一下&…

目标检测 | YOLOv5 训练自标注数据集实现迁移学习

Hi&#xff0c;大家好&#xff0c;我是源于花海。本文主要了解 YOLOv5 训练自标注数据集&#xff08;自行车和摩托车两种图像&#xff09;进行目标检测&#xff0c;实现迁移学习。YOLOv5 是一个非常流行的图像识别框架&#xff0c;这里介绍一下使用 YOLOv5 给使用 Labelme 标注…

【DevOps-07-2】Sonarqube基本使用

一、简要说明 Sonar Qube的使用方式很多&#xff0c;Maven可以整合&#xff0c;也可以采用sonar-scanner的方式&#xff0c;再查看Sonar Qube的检测效果 Sonarqube集成在Maven实现代码检测使用sonar-scanner客户端的方式 二、Sonarqube管理后台安装中文插件 1、登录Sonarqube管…

Vary: Scaling up the Vision Vocabulary for Large Vision-Language Models

ABSTRACT 现代大规模视觉-语言模型&#xff08;LVLMs&#xff09;采用了相同的视觉词汇-CLIP&#xff0c;可以涵盖大多数常见的视觉任务。然而&#xff0c;对于一些需要密集和细粒度视觉感知的特殊视觉任务&#xff0c;例如文档级OCR或图表理解&#xff0c;尤其是在非英语环境…

用通俗易懂的方式讲解:ChatGPT 开放的多模态的DALL-E 3功能,好玩到停不下来!

最近 ChatGPT 对 Plus 用户逐步开放一些多模态的功能&#xff0c;包括 &#xff08;图像生成&#xff09;、 GPT-4V&#xff08;图像识别&#xff09;等&#xff0c;很多网友乐此不疲地对这些新功能进行试用&#xff0c; 目前已经解锁了不少有趣的玩法&#xff0c;我将这些好玩…

软件测试/测试开发丨Vuetify框架的使用

介绍 Vuetify 是一个基于 Vue.js 精心打造 UI 组件库&#xff0c;整套 UI 设计为 Material 风格。能够让没有任何设计技能的开发者创造出时尚的 Material 风格界面。 为什么要使用Vuetify框架 所有组件遵从 Material Design 设计规范&#xff0c;UI 体验非常优秀&#xff0c…

FindMy技术用于键盘

键盘是我们生活中不可或缺的输入工具&#xff0c;是人与计算机之间沟通的桥梁&#xff0c;无论是编写文档、浏览网页、玩游戏、或是进行复杂的数据分析&#xff0c;键盘都在其中发挥着关键的作用。此外&#xff0c;键盘还是各种软件的快捷键操作的关键。通过熟练地运用快捷键&a…

【Axure高保真原型】树形表格_多选效果

今天和大家分享树形表格_多选效果的原型模板&#xff0c;点击树的箭头可以展开或者收起子节点&#xff0c;点击多选按钮可以选中或取消选择该行以及子级行内容&#xff0c;同时反选父级行内容&#xff0c;父级行内容能根据子级选中的数量自动反选&#xff0c;包括全选、半选和未…

数据结构 模拟实现LinkedList双向不循环链表

目录 一、双向不循环链表的概念 二、链表的接口 三、链表的方法实现 &#xff08;1&#xff09;display方法 &#xff08;2&#xff09;size方法 &#xff08;3&#xff09;contains方法 &#xff08;4&#xff09;addFirst方法 &#xff08;5&#xff09;addLast方法 …

网络连接 UDP2,UDP Connect, bind, send, recieve认知, -入门8

LWIP编程接口有RAW, NETCONN, SOCKET 2.UDP函数的理解 #define UDP_SERVER_PORT 8000 //PC side #define UDP_CLIENT_PORT 1234 // ctrl board side //PC IP address #define DEST_IP_ADDR0 192 #define DEST_IP_ADDR1 168 #define DEST_IP_ADDR2 3 #define DEST_IP_ADDR3 11…

2023-2024学年上学期算法设计与分析题期末考试模拟卷

2023-2024学年上学期算法设计与分析题期末考试模拟卷 文章目录 2023-2024学年上学期算法设计与分析题期末考试模拟卷单选题程序填空题输入格式:输出格式:输入样例1:输出样例1: 主观题 注意&#xff1a;该题集非标准答案&#xff0c;仅供参考&#xff0c;如果异议&#xff0c;请…

淘宝商品详情API接口(item_get-获得淘宝商品详情)主图,属性,sku,价格,搜索商品列表

淘宝开放平台提供了API接口&#xff0c;允许开发者获取淘宝商品的相关信息。为了获取商品详情&#xff0c;您可以使用 item_get API接口。以下是如何使用此API接口来获取商品的主图、属性、SKU、价格以及搜索商品列表的简要说明&#xff1a; 公共参数 名称类型必须描述keyStr…

SpringBoot Redis入门(一)——redis、Lettuce、Redisson使用

本章&#xff1a;将展示SpringBoot集成Redis三种客户端的配置要点和常见应用示例&#xff1b;下章&#xff1a;自行实现一个方法级的缓存注解&#xff0c;简化版的Cacheable&#xff0c;使初学者加深对Spring缓存框架的理解。 一、Lettuce客户端 Lettuce 是一种可扩展的、线程…

Java接口和抽象类的区别?

Java接口和抽象类的区别&#xff1f; Java接口和抽象类的含义&#xff1a; 接口&#xff08;Interface&#xff09;&#xff1a; 含义&#xff1a; 接口是一种抽象类型&#xff0c;它定义了一组抽象方法&#xff0c;但不能包含具体实现。接口可以包含常量和默认方法&#xff0c…

机器学习模型超参数优化,最频繁使用的5个工具包

优化超参数始终是确保模型性能最佳的关键任务。通常&#xff0c;网格搜索、随机搜索和贝叶斯优化等技术是主要使用的方法。 今天分享几个常用于模型超参数优化的 Python 工具包&#xff0c;如下所示&#xff1a; scikit-learn&#xff1a;使用在指定参数值上进行的网格搜索或随…

HarmonyOS应用开发学习笔记 包名、icon图标,应用名修改 UIAbility组件介绍、UIAbility启动模式、UIAbility组件基本用法

目前HarmonyOS应用主推的是Stage模型开发 一、Stage模型基本概念 项目描述UIAbility组件UIAbility组件是一种包含UI界面的应用组件&#xff0c;主要用于和用户交互。例如&#xff0c;图库类应用可以在UIAbility组件中展示图片瀑布流&#xff0c;在用户选择某个图片后&#xf…

Mongodb启动客户端 并简单演示切换数据库

上文 window下载安装Mongodb数据库 我们简单搭起了Mongodb的服务 但是 我们还需要一个客户端 我们还是将服务端启动起来 mongod --dbpath..\data\db我们再以管理员身份运行一个终端 直接进入 Mongodb安装目录的bin下 然后启动客户端的命令是 mongo --host 服务ip --port 服务…

Android studio环境配置

1.搜索android studio下载 Android Studio - Download 2.安装 3.配置环境 配置gradle&#xff0c;gradle参考网络配置。最后根据项目需求选择不同的jdk。

将PPT4页并排成1页

将PPT4页并排成1页打印 解决方法: 方法一 在打印时选择&#xff1a; 打开 PPT&#xff0c;点击文件选项点击打印点击整页幻灯片点击4张水平放置的幻灯平页面就会显示4张PPT显示在一张纸上 方法二 另存为PDF&#xff1a; 打开电脑上的目标PPT文件&#xff0c;点击文件点击…