搜索二叉树实现(非递归版本)

news2024/11/15 14:03:46

目录

一,搜索二叉树是个啥?

二,搜索二叉树的实现

1.前期工作

2.方法实现

1.插入

2,查找

3.删除

三,实现二叉搜索树的全部代码


一,搜索二叉树是个啥?

话不多所,先给各位来一个搜索二叉树:

从这棵树中可以看到这棵树有如下性质:

1.根节点的左节点的值小于根节点的值,根节点的右节点的值大于根节点的值。

2.这棵树的中序遍历的结果是一个升序的数组。

3.这棵树的左子树和右子树都是一颗搜索二叉树。

以上三点便是一棵搜索二叉树的性质!!!

二,搜索二叉树的实现

1.前期工作

     要实现一棵搜索二叉树,首先便要实现它的各个节点。实现如下:

template<class K>
struct BSNode
{
	BSNode(const K& key)
		:_left(nullptr)
		,_right(nullptr)
		,_key(key)
	{}

	BSNode<K>* _left;
	BSNode<K>* _right;
	K _key;
};

这个节点的成员就是它的左指针_left,右指针_right,还有这个节点里包含的一个值_key。

      接下来便要开始实现一下这棵树。实现如下:

template<class K>
class BSTree
{
  public:

  private:
    BSNode<K>* _root;
}

这棵树的成员便只有一个,那便是_root这个根节点。

2.方法实现

1.插入

在前期工作准备好以后便要来实现我们的方法了,现在来实现我们的插入方法。实现思路如下:

1.因为我们的_root是是私有的,所以我们不能实现一个需要传参的Insert方法。所以它必须是无参的。

2.要实现一个无参的方法,那我们就得套一层_Insert()方法在Insert()方法里面。

3.实现_Insert()方法步骤如下:

1.如果_root是nullptr便new一个节点,让_root接收这个新节点。

2.如果key比我当前的节点值要大,便向右走。

3.如果比我当前的节点值要小,那就向左走。

4.如果走到空(_root的替代值未为nullptr)那就在该位置生成一个新节点,并让该节点的parent的左或者右指针指向这个新节点。

代码如下:

实现无参:

bool Insert( const K& key)
	{
		return _Insert(key);
	}

_Insert()方法实现:

	bool _Insert(const K& key)
	{
		if (_root == nullptr)//若_root是一个nullptr那就给_root new 一个节点
		{
			_root = new BNode<K>(key);
			return true;
		}
		else
		{
			BNode<K>* cur = _root;
			BNode<K>* parent = nullptr;

			while (cur!=nullptr)//找位置
			{
				parent = cur;
				if (cur->_key < key)
				{
					cur = cur->_right;
				}
				else if (cur->_key > key)
				{
					cur = cur->_left;
				}
				else
				{
					return false;
				}
			}

			cur = new BNode<K>(key);//找到后便给这个位置new一个节点

			if (key > parent->_key)//判断一下是左边还是右边然后链接
			{
				parent->_right = cur;
			}
			else
			{
				parent->_left = cur;
			}
			return true;
		}
	    
	}
2,查找

   查找方法的实现也很简单,其实就是将_Insert()里面的查找代码给复制一份过来便可以了。同样的,我们的查找算法在类的外边也是不能调用_root的,所以也会有封装。Find()函数实现如下:

    bool Find(const K& key)
	{
		return _Find(key);
	}

     我们的_Find()函数实现如下:

       bool _Find(const K& key)
		{
			BNode<K>* cur = _root;
			while (cur)
			{
				if (key > cur->_key)
				{
					cur = cur->_right;
				}

				else if (key < cur->_key)
				{
					cur = cur->_left;
				}
				else
				{
					return true;
				}
			}

			return false;
		}
3.删除

删除方法的实现大概是最难写的一个代码了,首先我们得先找到这个要删除的节点找到以后分为三种情况讨论:

以如下搜索二叉树为例:

1.要删除节点的左节点为空。如以下情况:

比如要删除10这个节点,我们该如何操作呢?

我们的操作如下:

1.找到我的父亲。

2.判断我是父亲的那个节点。

3.如果我是父亲的左节点便让父亲的左节点连接到我的右节点上。如果我是父亲右节点,便让父亲的右节点指向我的右节点。

但是这里需要注意一个点:如果我是root,我便没有父亲。如以下情况

在这种情况下我们便可以直接让右节点担任root节点:

if (cur == _root)
{
   _root = _root->_right;
}

左节点为空的情况下实现删除代码如下:
 

                   if (cur->_left == nullptr)
					{
						if (cur == _root)
						{
							_root = _root->_right;
						}
						else
						{
							if (cur->_key > parent->_key)
							{
								parent->_right = cur->_right;
							}
							else
							{
								parent->_left = cur->_right;
							}
						}
						return true;
					}

2.要删除的节点的右节点为空。
其实这种情况下的的代码的值和前面的实现逻辑是一样的,

所以不解释直接给出实现代码:

                    else if (cur->_right == nullptr)
					{
						if (cur == _root)
						{
							_root = cur->_left;
						}
						else
						{
							if (cur->_key > parent->_key)
							{
								parent->_right = cur->_left;
							}
							else
							{
								parent->_left = cur->_left;
							}
						}
						return true;
					}

3.当我要删除的节点的左右两个节点都在

这个删除便是我们这个删除方法里面最难实现的一个代码,在这里我们要使用替换法来删除:

1.如何使用替换法呢?

步骤:1.定义parent和subLeft,subLeft定义为cur->right。

           记录我的父亲,这个父亲要初始化cur(当前节点)。

            2.找到当前节点的右子树的最左节点subLeft并更新parent位subLeft的父亲节点。

            3.交换cur节点和subLeft两个节点的值(使用swap).

            4.链接。

 了解完以上步骤以后写下如下代码:

                    else
					{//有两个孩子,替换法。(找右子树的最左节点)
						BNode<K>* Parent = cur;
						BNode<K>* SubLeft = cur->_right;

						while (SubLeft->_left)
						{
							Parent = SubLeft;
							SubLeft = SubLeft->_left;
						}
                        
						swap(cur->_key, SubLeft->_key);

						if (SubLeft == Parent->_left)
						{
							Parent->_left = SubLeft->_left;
						}
						else
						{
							Parent->_right = SubLeft->_left;
						}
						 
						return true;
					}

 在这里解释一下:

1.为什么parent要初始为cur,如以下例子:

               

假如是以上的情况,那我的这段代码是不会进去的:

while (SubLeft->_left)
{
	Parent = SubLeft;
	SubLeft = SubLeft->_left;
}

那如果我的parent 如果赋值为nullptr的话,那便会解引用nullptr:

if (SubLeft == Parent->_left)
{
  Parent->_left = SubLeft->_left;
}
else
{
  Parent->_right = SubLeft->_left;
}

所以我们必须要将parent初始化为cur。这个时候也能删除。

2.链接该如何连接?

我实现的连接代码是这样的:

if (SubLeft == Parent->_left)
{
  Parent->_left = SubLeft->_left;
}
else
{
  Parent->_right = SubLeft->_left;
}

在这里我们首先得先判断一下我们的subLeft是我的parent节点的哪一位?

可能是右节点:

就像我们上面的删除8的情况一样,我要删除的是根节点,我的根节点的左节点是nullptr。

也可能是左节点:

我进入了循环:

while (SubLeft->_left)
{
  Parent = SubLeft;
  SubLeft = SubLeft->_left;
}

三,实现二叉搜索树的全部代码

#include<iostream>
using namespace std;
#include<assert.h>


template<class K>
struct BNode
{
	BNode(const K& key)
		:_key(key)
		,_left(nullptr)
		,_right(nullptr)
	{}

	K _key;
	BNode<K>* _left;
	BNode<K>* _right;
};


template<class K>
class BSTree
{
public:

	bool Insert( const K& key)
	{
		return _Insert(key);
	}

	void Inorder()
	{
	 _Inorder(_root);
	 cout << endl;
	}

	bool Find(const K& key)
	{
		return _Find(key);
	}

	bool Erase(const K& key)
	{
		return _Erase(key);
	}
	 
private:
		bool _Insert(const K& key)
	{
		if (_root == nullptr)
		{
			_root = new BNode<K>(key);
			return true;
		}
		else
		{
			BNode<K>* cur = _root;
			BNode<K>* parent = nullptr;

			while (cur!=nullptr)
			{
				parent = cur;
				if (cur->_key < key)
				{
					cur = cur->_right;
				}
				else if (cur->_key > key)
				{
					cur = cur->_left;
				}
				else
				{
					return false;
				}
			}

			cur = new BNode<K>(key);
			if (key > parent->_key)
			{
				parent->_right = cur;
			}
			else
			{
				parent->_left = cur;
			}
			return true;
		}
	    
	}

		bool _Erase(const K& key)
		{
			assert(_root);
			BNode<K>* cur = _root;
			BNode<K>* parent = cur;


			while (cur)
			{
				if (key > cur->_key)
				{
					parent = cur;
					cur = cur->_right;
				}

				else if (key < cur->_key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else
				{
					if (cur->_left == nullptr)
					{
						if (cur == _root)
						{
							_root = _root->_right;
						}
						else
						{
							if (cur->_key > parent->_key)
							{
								parent->_right = cur->_right;
							}
							else
							{
								parent->_left = cur->_right;
							}
						}
						return true;
					}

					else if (cur->_right == nullptr)
					{
						if (cur == _root)
						{
							_root = cur->_left;
						}
						else
						{
							if (cur->_key > parent->_key)
							{
								parent->_right = cur->_left;
							}
							else
							{
								parent->_left = cur->_left;
							}
						}
						return true;
					}

					else
					{//有两个孩子,替换法。(找右子树的最左节点)
						BNode<K>* Parent = cur;
						BNode<K>* SubLeft = cur->_right;

						while (SubLeft->_left)
						{
							Parent = SubLeft;
							SubLeft = SubLeft->_left;
						}
                        
						swap(cur->_key, SubLeft->_key);

						if (SubLeft == Parent->_left)
						{
							Parent->_left = SubLeft->_left;
						}
						else
						{
							Parent->_right = SubLeft->_left;
						}
						 
						return true;
					}
				}
			}

			return false;



		}

		bool _Find(const K& key)
		{
			BNode<K>* cur = _root;
			while (cur)
			{
				if (key > cur->_key)
				{
					cur = cur->_right;
				}

				else if (key < cur->_key)
				{
					cur = cur->_left;
				}
				else
				{
					return true;
				}
			}

			return false;
		}

		void _Inorder(BNode<K>* root)
		{
			if (root == nullptr)
			{
				return;
			}

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



		BNode<K>*_root =nullptr ;
};

实际上还可以实现一个递归版本的二叉搜索树,有时间再写。

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

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

相关文章

查看linux下dns信息并修改

cat /etc/resolv.conf 查看dns 常用dns nameserver 114.114.114.114 nameserver 100.125.0.250 nameserver 8.8.8.8 nameserver 8.8.4.4 nameserver 192.168.122.1 nameserver 100.125.1.250 nameserver 100.125.1.251 nameserver 223.5.5.5 可以用vi 修改&#xff0c;修改后 …

【云原生】portainer管理多个独立docker服务器

目录 一、portainer简介 二、安装Portainer 1.1 内网环境下&#xff1a; 1.1.1 方式1&#xff1a;命令行运行 1.1.2 方式2&#xff1a;通过compose-file来启动 2.1 配置本地主机&#xff08;node-1&#xff09; 3.1 配置其他主机&#xff08;被node-1管理的节点服务器&…

spring常见问题汇总

1. 什么是spring? Spring是一个轻量级Java开发框架&#xff0c;最早有Rod Johnson创建&#xff0c;目的是为了解决企业级应用开发的业务 逻辑层和其他各层的耦合问题。它是一个分层的JavaSE/JavaEE full-stack&#xff08;一站式&#xff09;轻量级开源框架&#xff0c; 为开…

【LeetCode刷题日志】189.轮转数组

目录 1.题目描述 2.解题思路代码实现 方法一&#xff1a;使用额外的数组 解题思路&#xff1a; 代码实现&#xff1a; 方法二&#xff1a;环状替换 解题思路&#xff1a; 代码实现&#xff1a; 方法三&#xff1a;数组翻转 解题思路&#xff1a; 代码实现&#xff1a…

跨平台开发技术

目录 1.Qt1.简介2.优势3.劣势 2.NET CoreVue1.简介2.优点 3.Flutter1.简介2.优点3.缺点 4.Maui1.简介2.优点3.缺点 5.Avalonia1.简介2.优点3.缺点 6. Cordova1.简介2.优点3.缺点 7.Electron1.简介2.优点3.缺点 个人搜集资料并总结了一些跨平台开发技术&#xff0c;如有不足欢迎…

实战!RPA厂商选型分享

企业发展&#xff0c;必先科技先行&#xff0c;通过科技来提升内部运营、业务效率&#xff0c;从而达到降本、提质、增效的目标。但无论在技术选型亦或者厂商选择&#xff0c;都需要漫长的对比和调研&#xff0c;方能选择适合自身企业发展解决问题的技术和长期合作的技术伙伴。…

MathType7 公式编辑器嵌入Word\WPS,MathType 公式编辑常用小技巧

目录 1 MathType-7 下载 2 安装 4 嵌入word 5 嵌入wps 6 Mathtype 常用小技巧 6.1 四种插入公式的区别 &#xff1a; 6.2 MathType 常用快捷键 6.3 MathType转换公式 6.4 MathType公式编号 6.5 改变公式编号 6.6 mathtype 公式格式转换为latex格式 背景&#xff1a; w…

如何保卫您的网站:解决DDoS攻击与CC攻击

在当今数字化时代&#xff0c;网站安全是至关重要的。网络攻击如DDoS&#xff08;分布式拒绝服务&#xff09;和CC&#xff08;恶意请求洪水&#xff09;攻击可能会导致网站不稳定甚至不可用。本文将详细分析DDoS攻击和CC攻击的差异&#xff0c;以及如何使用CDN&#xff08;内容…

【C++】priority_queue仿函数

今天我们来学习C中另一个容器适配器&#xff1a;优先级队列——priority_queue&#xff1b;和C一个重要组件仿函数&#xff1a; 目录 一、priority_queue 1.1 priority_queue是什么 1.2 priority_queue的接口 1.2.1 priority_queue使用举例 二、仿函数 三、关于priority…

Linux C语言开发-D7D8运算符

算术运算符&#xff1a;-*/%&#xff0c;浮点数可以参与除法运算&#xff0c;但不能参与取余运算 a%b&#xff1a;表示取模或取余 关系运算符&#xff1a;<,>,>,<,,! 逻辑运算符:!,&&,|| &&,||逻辑运算符是从左到右&#xff0c;依次运算&#…

freeRTOS内部机制——栈的作用

上图中*pa 和*pb分别为R0&#xff0c;R1&#xff0c;调用C函数时&#xff0c;第一个参数保存在R0中第二个参数保存在R1中。这是约定。 指令保存在哪里&#xff1f; 指令保存在flash上面 LR等于什么? LR是返回地址&#xff0c;函数执行完了过后LR等于下一条指令的地址 运行…

JDK8新特性:Stream流

目录 1.获取Stream流 2.Stream流常见的中间方法 3.Stream流常见的终结方法 1、 Stream 是什么&#xff1f;有什么作用&#xff1f;结合了什么技术&#xff1f; ●也叫 Stream 流&#xff0c;是Jdk8开始新增的一套 API ( java . util . stream .*)&#xff0c;可以用于操作集…

【JAVA学习笔记】38 - 单例设计模式-静态方法和属性的经典使用

项目代码 https://github.com/yinhai1114/Java_Learning_Code/tree/main/IDEA_Chapter10/src/com/yinhai/final_ 一、什么是设计模式 1.静态方法和属性的经典使用 2.设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格以及解决问题的思考方式。设计模式就像是…

【完美世界】被骂国漫之耻,石昊人设战力全崩,现在真成恋爱世界了

【侵权联系删除】【文/郑尔巴金】 深度爆料&#xff0c;《完美世界》动漫第135集预告片已经更新了&#xff0c;但是网友们对此却是一脸槽点。从预告中可以看出&#xff0c;石昊在和战王战天歌的大战中被打成重伤&#xff0c;最后云曦也被战天歌抓住。在云曦面临生死危机的时候…

AIGC底层数据探索——高质量数据助力大模型迭代升级

// 编者按&#xff1a;近年来&#xff0c;大模型的概念逐渐受到更广泛的关注&#xff0c;而谈及大模型就离不开对底层数据的探索。 大模型训练数据痛点与中文数据集现状&#xff1b;高质量数据定义&#xff1b;对话式数据模型实验&#xff1b;晴数智慧高质量数据解决方案。 文…

信号补零对信号频谱的影响

文章目录 前言一、 什么是补零二、案例三、补零前仿真及分析1、补零前 MATLAB 源码2、仿真及结果分析①、 x n x_n xn​ 时域图②、 x n x_n xn​ 频谱图 四、补零后仿真及分析1、补6000个零且1000采样点①、 MATLAB 源码②、仿真及结果分析 2、波形分辨率3、补6000个零且7000采…

电子巡更和智能巡检关系

电子巡更和智能巡检是两种重要的安全巡查技术&#xff0c;它们之间相似相通。 电子巡更是一种基于传统巡更系统发展而来的技术&#xff0c;主要通过数字化手段对巡查工作进行记录和监督。它通常由巡更棒、信息钮和电子巡更软件组成。巡查人员在进行巡查时&#xff0c;需要携带…

Kafka集群搭建与SpringBoot项目集成

本篇文章的目的是帮助Kafka初学者快速搭建一个Kafka集群&#xff0c;以及怎么在SpringBoot项目中使用Kafka。 kafka集群环境包地址&#xff1a;百度网盘 请输入提取码 提取码&#xff1a;x9yn 一、Kafka集群搭建 1、准备环境 &#xff08;1&#xff09;准备三台…

泵站机电设备健康状态系统建立的关键

在现代工业运营中&#xff0c;泵站机电设备的健康管理至关重要。通过建立一套完善的泵站机电设备健康管理系统&#xff0c;可以有效地监测、诊断和维护设备&#xff0c;确保其正常运行和延长使用寿命。本文将从三个方面展开讨论&#xff0c;分别是泵站机电设备养护在设备健康管…

题目 1053: 二级C语言-平均值计算(python详解)——练气三层初期

✨博主&#xff1a;命运之光 &#x1f984;专栏&#xff1a;算法修炼之练气篇&#xff08;C\C版&#xff09; &#x1f353;专栏&#xff1a;算法修炼之筑基篇&#xff08;C\C版&#xff09; &#x1f352;专栏&#xff1a;算法修炼之练气篇&#xff08;Python版&#xff09; ✨…