【C++】二叉搜索树的模拟实现(K,KV树)递归与非递归方式

news2024/12/24 2:52:22

文章目录

  • 前言
  • 一、K树
    • 1.结点的定义
    • 2.构造函数
    • 3.拷贝构造函数
    • 4.赋值运算符重载
    • 5.析构函数
    • 6.二叉搜索树的查找(find)
      • 1.非递归
      • 2.递归
    • 7.二叉搜索树的插入(Insert)
      • 1.非递归
      • 2.递归
    • 8.二叉搜素树的删除(Erase)
      • 1.非递归
      • 2.递归
    • 9.中序遍历(InOrder)
  • 二、KV树
  • 二叉搜索树性能


前言

在这里插入图片描述

一、K树

K模型:K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到的值。

1.结点的定义

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

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

2.构造函数

template<class K>
class BSTree {
	typedef BSTreeNode<K> Node;
public:
	BSTree() {
		_root = nullptr;
	}

3.拷贝构造函数

BSTree(const BSTree<K>& t) {
		_root = Copy(t._root);
	}
Node* Copy(Node* root) {
		if (root == nullptr) {
			return nullptr;
		}
		//递归进行拷贝
		Node* copyroot = new Node(root->_key);
		copyroot->_left = Copy(root->_left);
		copyroot->_right = Copy(root->_right);
		return copyroot;
	}

4.赋值运算符重载

BSTree<K>& operator=(BSTree<K> t) {
//先拷贝出t,让_root指向t的位置,进行交换
//后续调用析构函数直接析构t._root
		swap(_root, t._root);
		return *this;
	}

5.析构函数

~BSTree() {
		Destroy(_root);
	}
	void Destroy(Node*& root) {
		if (root == nullptr) {
			return;
		}
		Destroy(root->_left);
		Destroy(root->_right);
		delete root;
		root = nullptr;
	}

6.二叉搜索树的查找(find)

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

1.非递归

bool Find(const K& key) {
		Node* cur = _root;
		while (cur) {
			if (cur->_key < key) {
				//当前值比要查找的值小
				//到右树去查找
				cur = cur->_right;
			}
			else if (cur->_key > key) {
				cur = cur->_left;
			}
			else {
				//相等,说明找到了
				return true;
			}
		}
		return false;
	 }

2.递归

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;
		}
	}

7.二叉搜索树的插入(Insert)

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

1.非递归

bool Insert(const K& key) {
		if (_root == nullptr) {
			_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 {
				//树中已经有key值不能再插入
				return false;
			}
		}
		cur = new Node(key);//cur为要插入结点
		if (parent->_key < key) {
			//判断cur插在父结点的左数还是右树
			parent->_right = cur;
		}
		else {
			parent->_left = cur;
		}
		return true;//插入成功
	 }

2.递归

在这里插入图片描述

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;}
	}

8.二叉搜素树的删除(Erase)

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

在这里插入图片描述

在这里插入图片描述

1.非递归

	bool Erase(const K& key) {
		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 {//找到要删除结点
				//1.   要删除节点左边为空
				if (cur->_left == nullptr) {
					if (cur == _root) {
						//要删除结点为根节点,则改变根节点位置
						_root = _root->_right;
					}
					else {
						//判断要删除结点在父结点的左子树还是右子树
						if (parent->_right == cur) {
							//在父结点右子树则让其指向删除结点的右子树
							parent->_right = cur->_right;
						}
						else {
							parent->_left = cur->_right;
						}
					}
				}
				//2.     要删除节点右边为空
				else if (cur->_right == nullptr) {
					if (cur == _root) {
						//要删除结点为根节点,则改变根节点位置
						_root = _root->_left;
					}
					else {
						//判断要删除结点在父结点的左子树还是右子树
						if (parent->_right == cur) {
							parent->_right = cur->_left;
						}
						else {
							parent->_left = cur->_left;
						}
					}
				}
				else {//3.要删除结点左右子树都不为空
					Node* parent = cur;
					Node* leftMax = cur->_left;
					//寻找可替代结点,替代过好要满足二叉搜索树的性质
					//要删除结点左子树的最大值,或者右子树的最小值
					while (leftMax->_right) {
						//寻找左子树的最大值
						parent = leftMax;
						leftMax = leftMax->_right;
					}
					swap(leftMax->_key, cur->_key);
					//替代结点与被删除结点的值交换
					//这样leftMax就为要被删除的结点
					if (parent->_left == leftMax) {
						//判断leftMax在父结点的左子树还是右子树
						parent->_left = leftMax->_left;
					}
					else {
						parent->_right = leftMax->_left;
					}//改变指向
					cur = leftMax;
				}
				delete cur;
				return true;
			}	
		}
		return false;
 }

2.递归

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) {
			return _Erase(root->_right, key);
		}
		else if (root->_key > key) {
			return _Erase(root->_left, key);
		}//寻找要删除结点
		else {
			Node* del = root;
			//1. 要删除节点左边为空
			if (root->_left == nullptr) {
				root = root->_right;
			}
			//2. 要删除节点右边为空
			else if (root->_right == nullptr) {
				root = root->_left;
			}
			else {
				//3.要删除结点左右都不为空
				Node* leftMax = root->_left;
				while (leftMax->_right) {
					//寻找替代结点
					leftMax = leftMax->_right;
				}
				swap(root->_key, leftMax->_key);
				//交换值后,要删除的结点为leftMax,其在root的左子树
				return _Erase(root->_left, key);
			}
			delete del;
			return true;
		}
	}

9.中序遍历(InOrder)

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

二、KV树

KV模型:每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对

namespace key_value {
	template<class K, class V>
	struct BSTreeNode
	{
		BSTreeNode<K, V>* _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 BSTreeNode<K, V> Node;
	public:
		BSTree() {
			_root = nullptr;
		}

		BSTree(const BSTree<K, V>& t) {
			_root = Copy(t._root);
		}

		BSTree<K, V>& operator=(BSTree<K, V> t) {
			swap(_root, t._root);
			return *this;
		}

		~BSTree() {
			Destroy(_root);
		}



		bool InsertR(const K& key, const V& value) {
			return _InsertR(_root, key, value);
		}

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

		Node* FindR(const K& key) {
			return 	_FindR(_root, key);
		}
		void InOrder() {
			_InOrder(_root);
			cout << endl;
		}



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

		void Destroy(Node*& root) {
			if (root == nullptr) {
				return;
			}
			Destroy(root->_left);
			Destroy(root->_right);
			delete root;
			root = nullptr;
		}

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

		}

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

		bool _Erase(Node*& root, const K& key) {
			if (root == nullptr) {
				return false;
			}
			if (root->_key < key) {
				return _Erase(root->_right, key);
			}
			else if (root->_key > key) {
				return _Erase(root->_left, key);
			}
			else {
				Node* del = root;
				if (root->_left == nullptr) {
					root = root->_right;
				}
				else if (root->_right == nullptr) {
					root = root->_left;
				}
				else {
					Node* leftMax = root->_left;
					while (leftMax->_right) {
						leftMax = leftMax->_right;
					}
					swap(root->_key, leftMax->_key);
					return _Erase(root->_left, key);
				}
				delete del;
				return true;
			}
		}


		void _InOrder(Node* root)
		{
			if (root == NULL){return;}
			_InOrder(root->_left);
			cout << root->_key << ":" << root->_value << endl;
			_InOrder(root->_right);
		}

	private:
		Node* _root;
	};
}

二叉搜索树性能

在这里插入图片描述

最高找高度次O(N)

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

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

相关文章

腾讯云轻量应用服务器介绍

腾讯云轻量应用服务器开箱即用、运维简单的轻量级云服务器&#xff0c;CPU内存带宽配置高并且成本低&#xff0c;轻量2核2G3M带宽、2核2G4M带宽、2核4G5M带宽、4核8G12M带宽&#xff0c;还有8核16G18M和16核32G28M配置可选&#xff0c;腾讯云服务器网分享腾讯云轻量应用服务器详…

Java 诊断工具 arthas-boot 安装步骤及常用命令

arthas 是 Alibaba 开源的 Java 诊断工具&#xff0c;它的特点是使用方便&#xff0c;功能强大。最重要的是&#xff0c;arthas可以监察生产环境下的项目&#xff0c;使用的过程无需重启项目。 官方文档&#xff1a;https://alibaba.github.io/arthas 一、安装 第1步&#xf…

Jpa与Druid线程池及Spring Boot整合(一): spring-boot-starter-data-jpa 搭建持久层

(一)Jpa与Druid连接池及Spring Boot整合作为持久层,遇到系列问题,下面一 一记录&#xff1a; pom.xml 文件中加入必须的依赖: <!--设置spring-boot依赖的版本 --> <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot…

Kafka 01——Kafka的安装及简单入门使用

Kafka 01——Kafka的安装及简单入门使用 1. 下载安装1.1 JDK的安装1.2 Zookeeper的安装1.2.1 关于Zookeeper版本的选择1.2.2 下载、安装Zookeeper 1.3 kafka的安装1.3.1 下载1.3.2 解压1.3.3 修改配置文件 2. 启动 kafka2.1 Kafka启动2.2 启动 kafka 遇到的问题2.2.1 问题12.2.…

2453. 摧毁一系列目标;2121. 相同元素的间隔之和;2075. 解码斜向换位密码

2453. 摧毁一系列目标 核心思想&#xff1a;找出与space取模一样的最多的数中的最小值。 2121. 相同元素的间隔之和 核心思想&#xff1a;与相同元素的等距离问题一模一样。我们首先将相同值的坐标统计出来&#xff0c;然后对于a[i]的和为s,对于a[i-1]的和为s&#xff0c;s-s…

ardupilot 为什么要采样四元数姿态控制

目录 文章目录 目录摘要1.姿态控制为什么要用到四元数2.四元数姿态控制摘要 本节主要说明清楚ardupilot姿态控制为什么要用到四元数,欢迎批评指正!!! 1.姿态控制为什么要用到四元数 对于ardupilot的姿态控制中主要用PID控制算法,姿态控制采用串级PID控制。主要包含:外环…

D. More Wrong Codeforces Round 890 (Div. 2) 1856D

Problem - D - Codeforces 题目大意&#xff1a;有一个隐藏的排列&#xff0c;给出其长度n&#xff0c;每次可以询问一个[l,r]区间内有多少逆序对&#xff0c;费用为&#xff0c;要求在总费用不超过的情况下输出最大值的下标 2<n<2000 思路&#xff1a;首先可以发现如…

录音转文字神器帮你轻松转写录音内容

你是否曾经需要将会议、讲座或采访录音转换成文字&#xff0c;却不知道如何转写录音内容更加快速准确&#xff1f;那你来对地方了&#xff01;录音转文字功能的诞生将让你的录音转写工作变得更加简单。如果你也还不清楚免费录音转文字软件有哪些的话&#xff0c;那现在就跟我一…

day2 开发环境平台搭建

Corter - M0存储器映射 SOC中CPU访问各个外部部件是通过每个部件的编号&#xff08;地址&#xff09;访问的&#xff1b; CPU给各个部件分配地址的过程称之为映射&#xff1b; Corter - M0 为32位&#xff0c;所以其给部件分配的地址范围为0 - 4Gb&#xff08;2^32&#xff0…

TZOJ 3100: 女生寝室的围墙 凸包

凸包模板题&#xff1a; 在平面上能包含所有给定点的最小凸多边形叫做凸包。 实际上可以理解为用一个橡皮筋包含住所有给定点的形态。 凸包用最小的周长围住了给定的所有点。如果一个凹多边形围住了所有的点&#xff0c;它的周长一定不是最小&#xff0c;如下图。根据三角不…

设备健康度评价实践--煤磨机

煤磨健康度评价说明 煤磨健康度评价模型 在以可靠性为中心的维护理论中,一般用健康度量化描述设备的健康状态,它是指在规定的时间和条件下,设备持续完成预定功能的能力大小,是表达设备或零件状态好坏的定量指标。 煤磨的健康状态评价原则是要求能够真实地反映磨机的健康状态…

lancet: 【推荐】--源码学习

一个全面、高效、可复用的go语言工具函数库&#xff1b; 可以学习源码的好的地方&#xff0c;这个是个工具库&#xff0c;建议最好的办法是 在项目中导入后&#xff0c;然后查看他的各个源代码进行学习使用 golangd中&#xff0c;查看导入包以及他的源代码&#xff1b; 中文…

农历日程提醒软件哪个好?安卓苹果通用的日程提醒软件

在日常生活中&#xff0c;有很多重要的日期都是按照农历&#xff08;阴历&#xff09;计算的&#xff0c;例如春节、七夕、中秋节等重要的传统节日、一些亲朋好友的生日、农村乡镇农历逢一、逢七赶集等。为了防止自己忘记这些重要的节日&#xff0c;提前做好准备&#xff0c;很…

【深度学习笔记】TensorFlow 常用函数

TensorFlow 提供了一些机器学习中常用的数学函数&#xff0c;并封装在 Module 中&#xff0c;例如 tf.nn Module 提供了神经网络常用的基本运算&#xff0c;tf.math Module 则提供了机器学习中常用的数学函数。本文主要介绍 TensorFlow 深度学习中几个常用函数的定义与用法&…

Flask 框架集成Bootstrap

前面学习了 Flask 框架的基本用法&#xff0c;以及模板引擎 Jinja2&#xff0c;按理说可以开始自己的 Web 之旅了&#xff0c;不过在启程之前&#xff0c;还有个重要的武器需要了解一下&#xff0c;就是著名的 Bootstrap 框架和 Flask 的结合&#xff0c;这将大大提高开发 Web …

【ROS】Ubuntu18.04安装Ros

Ubuntu18.04安装Ros 引言1 ROS安装&#xff08;一键式&#xff09;2 正常安装2.1 添加ROS软件源2.2 添加公钥2.3 更新2.4 安装ros2.5 初始化 rosdep2.6 设置环境2.7 安装rosinstall,便利的工具2.8 检验 3 rviz将bag数据可视化为点云3.1 打开ROS和rviz软件3.2 配置rviz软件可视化…

gpu-manager安装及测试

提示&#xff1a;GPU-manager安装为主部分内容做了升级开箱即用&#xff0c;有用请点收藏❤抱拳 文章目录 前言一、约束条件二、使用步骤1.下载镜像1.1 查看当前虚拟机的驱动类型&#xff1a; 2.部署gpu-manager3.部署gpu-admission4.修改kube-scheduler.yaml![在这里插入图片描…

Web Server市场占有率调查

目录 一、理论 1.Web Server市场占有率调查 一、理论 1.Web Server市场占有率调查 &#xff08;1&#xff09;netcraft ①查询 每月netcraft公司都会出一次调查报告 netcraft官方&#xff1a;Netcraft | Leader in Phishing Detection, Cybercrime Disruption and Websi…

Samba(二)

问题 Rocky Linux使用smbclient访问win11的共享文件时提示 Error NT_STATUS_IO_TIMEOUT 分析 通过测试&#xff0c;发现关闭windows公用网络防火墙时&#xff0c;可正常显示服务器端所分享出来的所有资源&#xff1b;进一步发现单独放行防火墙进站规则中的文件和打印机共享&a…

B树的插入与删除过程

B树的插入 原树&#xff1a; 插入key后&#xff0c;若导致原节点关键字数超过上限&#xff0c;则从中间位置&#xff08; ⌈ m 2 ⌉ \lceil\frac{m}{2}\rceil ⌈2m​⌉&#xff09;将关键字分成两部分&#xff0c;左部分包含的关键字放在原节点中&#xff0c;右部分包含的关键…