二叉树进阶 - (C++二叉搜索树的实现)

news2025/1/21 21:52:41

二叉树进阶 - (二叉搜索树的实现)

  • 二叉搜索树
    • 1. 二叉搜索树概念
    • 2. 二叉搜索树操作
      • 2.1 二叉搜索树的查找
      • 2.2 二叉搜索树的插入
      • 2.3 二叉搜索树的删除(重点)
    • 3. 二叉搜索树的(代码)实现

二叉搜索树

1. 二叉搜索树概念

二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:

  • 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  • 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  • 它的左右子树也分别为二叉搜索树
    在这里插入图片描述
    在这里插入图片描述

2. 二叉搜索树操作

在这里插入图片描述

int a[] = {8, 3, 1, 10, 6, 4, 7, 14, 13};

2.1 二叉搜索树的查找

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

从根节点开始,用 cur 指针指向当前节点。
在遍历过程中,比较要查找的值 key 与当前节点的值 cur->_key 的大小关系,根据比较结果来决定继续在左子树还是右子树中查找。
如果要查找的值 key 大于当前节点的值 cur->_key,则继续在当前节点的右子树中查找。
如果要查找的值 key 小于当前节点的值 cur->_key,则继续在当前节点的左子树中查找。
如果要查找的值 key 等于当前节点的值 cur->_key,表示找到了该值,直接返回 true。
当遍历到叶子节点时,仍然没有找到要查找的值,表示该值不存在于树中,返回 false。

2.2 二叉搜索树的插入

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

首先判断根节点 _root 是否为空,如果为空,则将新节点作为根节点,并返回插入成功。
如果根节点不为空,则从根节点开始遍历树来查找合适的插入位置。
在遍历过程中,使用 cur 指针来指向当前节点,parent 指针来指向当前节点的父节点。
如果要插入的值 key 大于当前节点的值 cur->_key,则继续在当前节点的右子树中查找。
如果要插入的值 key 小于当前节点的值 cur->_key,则继续在当前节点的左子树中查找。
如果要插入的值 key 等于当前节点的值 cur->_key,表示值已经存在于树中,不进行插入,直接返回插入失败。
当遍历到叶子节点时,将新节点插入到当前节点的左子树或右子树,根据新节点的值与当前节点的值的大小关系来决定插入到左子树还是右子树。
插入完成后,返回插入成功。

2.3 二叉搜索树的删除(重点)

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

  • 情况b:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点–直接删除
  • 情况c:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点–直接删除
  • 情况d:在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点中,再来处理该结点的删除问题–替换法删除
    在这里插入图片描述

从根节点开始,用 cur 指针指向当前节点,parent 指针指向当前节点的父节点。
在遍历过程中,比较要删除的值 key 与当前节点的值 cur->_key 的大小关系,根据比较结果来决定继续在左子树还是右子树中查找要删除的节点,并同时记录当前节点的父节点。
如果要删除的值 key 大于当前节点的值 cur->_key,则继续在当前节点的右子树中查找,并更新父节点指针。
如果要删除的值 key 小于当前节点的值 cur->_key,则继续在当前节点的左子树中查找,并更新父节点指针。
如果要删除的值 key 等于当前节点的值 cur->_key,表示找到了要删除的节点。
根据要删除节点的左右子树情况,执行不同的删除操作:
如果要删除的节点的左子树为空,将父节点的右指针指向要删除节点的右子树。
如果要删除的节点的右子树为空,将父节点的左指针指向要删除节点的左子树。
如果要删除的节点的左右子树都不为空,找到要删除节点右子树中的最小节点,将要删除节点的值替换为最小节点的值,然后删除最小节点。
返回 true 表示删除成功。
如果遍历完整棵树都没有找到要删除的节点,返回 false 表示删除失败。

3. 二叉搜索树的(代码)实现


非递归实现

#pragma once
#include <iostream>
using namespace std;
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:
	//插入
	bool Insert(const K& key)
	{
		if (_root == nullptr)
		{
			_root = new Node(key);
			return true;
		}
		Node* cur = _root;
		Node* parent = nullptr;
		while (cur)
		{
			parent = cur;
			if (key > cur->_key)
			{
				cur = cur->_right;
			}
			else if (key < cur->_key)
			{
				cur = cur->_left;
			}
			else
			{
				return false;
			}
		}
		cur = new Node(key);
		if (key > parent->_key)
		{
			parent->_right = cur;
		}
		if (key < parent->_key)
		{
			parent->_left = cur;
		}
		return true;
	}
	//中序打印
	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}
	//查找
	void Find(const K& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (key > cur->_key)
			{
				cur = cur->_right;
			}
			else if (key < cur->_key)
			{
				cur = cur->_left;
			}
			else
			{
				return true;
			}
		}
		return false;
	}
	//删除
	bool Erase(const K& key)
	{
		Node* cur = _root;
		Node* parent = nullptr;
		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 = cur->_right;
					}
					else if (cur == parent->_left)
					{
						parent->_left = cur->_right;
					}
					else
					{
						parent->_right = cur->_right;
					}
				}
				else if (cur->_right == nullptr)
				{//右为空
					if (cur == _root)
					{
						_root = cur->_left;
					}
					else if (cur == parent->_left)
					{
						parent->_left = cur->_left;
					}
					else
					{
						parent->_right = cur->_left;
					}
				}
				else
				{//左右都不为空
					Node* SubLeft = cur->_right;
					Node* _parent = cur;
					while (SubLeft->_left)
					{
						_parent = SubLeft;
						SubLeft = SubLeft->_left;
					}
					swap(cur->_key, SubLeft->_key);

					if (SubLeft == _parent->_left)
					{
						_parent->_left = SubLeft->_right;
					}
					else
					{
						_parent->_right = SubLeft->_right;
					}
				}
				return true;
			}
		}
		return false;
	}
	//析构
	~BSTree()
	{
		Destroy(_root);
	}
	//BSTree = default;
	BSTree()
	{}
	//拷贝构造
	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 0;
		}
		Node* NewRoot = new Node(root->_key);
	
		NewRoot->_left = Copy(root->_left);
		NewRoot->_right = Copy(root->_right);
		return NewRoot;
	}

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

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

private:
	Node* _root = nullptr;
};


递归实现

#pragma once
#include <iostream>
using namespace std;
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:
	
	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);
	}
	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}
		//析构
	~BSTree()
	{
		Destroy(_root);
	}
	//BSTree = default;
	BSTree()
	{}
	//拷贝构造
	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 0;
		}
		Node* NewRoot = new Node(root->_key);
	
		NewRoot->_left = Copy(root->_left);
		NewRoot->_right = Copy(root->_right);
		return NewRoot;
	}

	void Destroy(Node*& root)
	{
		if (root == nullptr)
		{
			return;
		}
		return Destroy(root->_left);
		return Destroy(root->_right);
		delete root;
		root = nullptr; 
	}
	//递归实现删除
	bool _EraseR(Node*& root, const K& key)
	{
		if (root == nullptr)
		{
			return false;
		}
		if (key < root->_key)
		{
			return _EraseR(root->_left, key);
		}
		else if (key > root->_key)
		{
			return _EraseR(root->_right, key);
		}
		else
		{
			if (root->_left == nullptr)
			{
				Node* del = root;
				root = root->_right;
				delete del;
				return true;

			}
			else if (root->_right == nullptr)
			{
				Node* del = root;
				root = root->_left;
				delete del;
				return true;
			}
			else
			{
				Node* SubLeft = root->_right;
				while (SubLeft->_left)
				{
					SubLeft = SubLeft->_left;
				}
				swap(root->_key, SubLeft->_key);

				//转换在子树中去递归删除   
				return _EraseR(root->_right, key);
			}
		}
	}
	//递归实现插入
	bool _InsertR(Node*& root, const K& key)
	{
		if (root == nullptr)
		{
			root = new Node(key);
			return true;
		}
		if (key > root->_key)
		{
			return _InsertR(root->_right, key);
		}
		if (key < root->_key)
		{
			return _InsertR(root->_left, key);
		}
		else
		{
			return false;
		}
	}
	//递归实现查找
	bool _findR(Node* root, const K& key)
	{
		if (root == nullptr)
		{
			return false;
		}
		else if (key > root->_key)
		{
			return _findR(root->_right, key);
		}
		else if (key < root->_key)
		{
			return _findR(root->_left, key);
		}
		else
		{
			return true;
		}
	}
	//中序遍历打印
	void _InOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}
		_InOrder(root->_left);
		cout << root->_key << " ";
		_InOrder(root->_right);
	}

private:
	Node* _root = nullptr;
};

(本章完)

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

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

相关文章

MySQL是如何优化in子查询的?

目录 前言 一、普通in子查询 二、物化表 三、SQL优化 四、IN语句的优化方式 1. 使用子查询代替IN查询 2. 使用JOIN代替IN查询 3. 使用EXISTS代替IN查询 4. 使用索引优化IN查询 5. 优化查询语句 总结 前言 对于很多的开发小伙伴来说&#xff0c;在MySQL中进行in子查…

Antlr4学习笔记

目录 背景 相关概念 流程说明 入门示例 简易计算器 环境准备 开发 java8方法提取 语法规则 常用的语法要点 设计语法 序列模式 选择模式 词法符号依赖 嵌套模式 总结 参考资料 背景 在阅读shardingjdbc-4.1.1代码时&#xff0c;发现一段sql解析的逻辑&#x…

极智开发 | H100服务器的庐山真面目

欢迎关注我的公众号 [极智视界],获取我的更多经验分享 大家好,我是极智视界,本文分享一下 H100服务器的庐山真面目。 邀您加入我的知识星球「极智视界」,星球内有超多好玩的项目实战源码和资源下载,链接:https://t.zsxq.com/0aiNxERDq H100 是英伟达最强显卡,当然其实也…

stable-diffusion 电商领域prompt测评集合

和GhostReivew一个思路&#xff0c;还是从比较好的图片或者是civitai上找一些热门的prompt&#xff0c;从小红书上也找到了不少的prompt&#xff0c;lexica.art上也有不少&#xff0c;主要是为了电商场景的一些测评&#xff1a; 小红书、civitai、Lexica、Liblib.ai、 depth o…

基于GEE云平台一种快速修复Landsat影像条带色差的方法

这是之前关于去除遥感影像条带的另一篇文章&#xff0c;因为出版商推迟了一年发布&#xff0c;所以让大家久等了。这篇文章的主要目的是对Landsat系列卫星因为条带拼接或者镶嵌产生的条带来进行的一种在线修复方式。 原文连接 一种快速修复Landsat影像条带色差的方法 题目&a…

app开发之后需要做什么

在完成app的开发之后&#xff0c;还有一系列的工作需要进行&#xff0c;以确保app的顺利上线和用户的良好体验。下面将从原理和详细介绍两个方面来介绍app开发之后需要做的工作。 一、原理介绍 1. 测试与调试&#xff1a;在app开发完成后&#xff0c;需要进行全面的测试与调试…

防范欺诈GPT

去年&#xff0c;ChatGPT的发布让全世界都感到惊讶和震惊。 突然间出现了一个平台&#xff0c;它比之前的任何其他技术都更深入地了解互联网。人工智能可以被训练成像阿姆一样说唱&#xff0c;以世界著名诗人的风格写作&#xff0c;并精确地翻译内容&#xff0c;以至于它似乎能…

初识Vue 解决vue在启动时生成的提示

让我为大家简单介绍一下吧&#xff01; Vue是一套用于构建用户界面的渐进式javaScript框架 当我们引入vue.js后 <script src"../js/vue.js"></script>我们发现&#xff0c;当我们打开网页时&#xff0c;控制台会出现以下内容 那我们该怎么解决呢&…

思科网络基础

目录 一、特殊的ip地址 1.一些基本概念 2.私有地址 3.子网划分 4.VLSM&#xff08;可变长子网掩码&#xff09; 5.CIDR&#xff08;无类域间路由-超网&#xff09; 二、IP头和一些基本概念 1.ip头 2.mtu 3.免费arp 一、特殊的ip地址 1.一些基本概念 网络位不变&…

卡尔曼家族从零解剖-(04)贝叶斯滤波→细节讨论,逻辑梳理

讲解关于slam一系列文章汇总链接:史上最全slam从零开始&#xff0c;针对于本栏目讲解的 卡尔曼家族从零解剖 链接 :卡尔曼家族从零解剖-(00)目录最新无死角讲解&#xff1a;https://blog.csdn.net/weixin_43013761/article/details/133846882 文末正下方中心提供了本人 联系…

玩转AIGC:如何选择最佳的Prompt提示词?

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

Spring、SpringMVC、Mybatis

一.Spring基础 1.Spring 框架是什么 Spring 是一款开源的轻量级 Java 开发框架&#xff0c;我们一般说 Spring 框架指的都是 Spring Framework&#xff0c;它是很多模块的集合&#xff0c;例如&#xff0c;Spring-core、Spring-JDBC、Spring-MVC 等&#xff0c;使用这些模块可…

Vite 的基本原理,和 webpack 在开发阶段的比较

目录 1&#xff0c;webpack 的流程2&#xff0c;Vite 的流程简单编译 3&#xff0c;总结 主要对比开发阶段。 1&#xff0c;webpack 的流程 开发阶段大致流程&#xff1a;指定一个入口文件&#xff0c;对相关的模块&#xff08;js css img 等&#xff09;先进行打包&#xff0…

【MySql】11- 实践篇(九)

文章目录 1. 大查询是否会把数据库内存打爆?1.1 全表扫描对 server 层的影响1.2 全表扫描对 InnoDB 的影响 2. 可不可以使用join?2.1 Index Nested-Loop Join2.2 Simple Nested-Loop Join2.3 Block Nested-Loop Join 3. join语句怎么优化?3.1 Multi-Range Read 优化3.2 Batc…

安装Oracle 11g Error in invoking target报错

在redhat7.5上安装Oracle 11g&#xff0c;安装过程中到86%时出现Error in invoking target报错 原因是由于操作系统版本过高&#xff0c;导致lib链接报错 [oracleemrtest ~]$ cd O R A C L E H O M E / s y s m a n / l i b / [ o r a c l e e m r t e s t l i b ] ORACLE…

没有PDF密码,如何解密文件?

PDF文件有两种密码&#xff0c;一个打开密码、一个限制编辑密码&#xff0c;因为PDF文件设置了密码&#xff0c;那么打开、编辑PDF文件就会受到限制。想要解密&#xff0c;我们需要输入正确的密码&#xff0c;但是有时候我们可能会出现忘记密码的情况&#xff0c;或者网上下载P…

canvas制作电子白板签名功能

Canvas是html5主要的画图工具&#xff0c;用户可以利用js在里面构思自己的创意&#xff0c;页面上很多手写签名是通过这个来完成的&#xff0c;让我们来用一个简单的例子作为抛砖引玉。 效果图 源代码 <html> <head> <meta charset"utf-8"> &l…

【脚本工具】视频抽帧、添加srt字幕朗读、添加背景音频

1.文章目录 看完本文章&#xff0c;你将能学会一下内容&#xff1a; 批量视频抽帧&#xff1b;添加srt字幕&#xff1b;添加srt配音&#xff1b;添加背景音乐&#xff1b;多视频片段合成一个新视频&#xff1b; 效果&#xff1a; 2.安装依赖 首先安装视频处理库opencv-pyth…

ERR invalid password

E:\Document_Redis_Windows\redis-2.4.5-win32-win64\64bit redis.conf

前端实现调用打印机和小票打印(TSPL )功能

Ⅰ- 壹 - 使用需求 前端 的方式 点击这个按钮&#xff0c;直接让打印机打印我想要的东西 Ⅱ - 贰 - 小票打印 目前比较好的方式就是直接用 TSPL 标签打印指令集, 基础环境就不多说了,这个功能的实现就是利用usb发送指令,现在缺少个来让我们能够和usb沟通的工具,下面这就是推…