【C++进阶03】二叉搜索树

在这里插入图片描述

一、二叉搜索树的概念和性质

中序遍历二叉搜索树会得到一个有序序列
所以二叉搜索树又称二叉排序树
它可以是一棵空树
也可以是具有以下性质的二叉树:

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

二叉搜索树没有相同值的节点
二叉搜索树支持增删查,不支持改
修改会破坏二叉搜索树跟节点比左子树大
右子树小的结构

如图:
在这里插入图片描述

二、二叉搜索树的模拟实现

二叉搜索树节点

// BSTree.h
#pragma once

// BinarySearchTree --- BSTree
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; // 名字过长在类里面再typedef,类里受类域限制不会名字冲突
public:
	bool Insert(const K& key) // 要插入节点内容重复,插入失败返回false
	{
		if (_root == nullptr)
		{
			_root = new Node(key); // 没写构造函数会导致无法将参数 1 从“const K”转换为“const BSTreeNode<K> &”,new的时候会以为你要调转换
			return true;
		}

		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
			{
				return false;
			}
		}

		cur = new Node(key);
		// 链接
		if (parent->_key < key)
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}
		
		return true;
	}

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

	void InOrder() // 中序需要传_root根节点,在类外无法直接访问,所以再套一层。不能用缺省值解决
	{
		_InOrder(_root);
	}

	// void _InOrder(Node* root = _root) // 缺省值必须是全局变量或是常量,访问_root得用this,而this只能在函数内部使用
	void _InOrder(Node* root) // 中序
	{
		if (root == nullptr)
			return;

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

private:
	Node* _root = nullptr;
};

插入的递归实现

bool _InsertR(Node*& root, const K& key) // 用&解决链接问题,最后走到nullptr的位置是上一个节点的别名,很巧妙的链接上
{
	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;
	}
}

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

查找的递归实现

bool _FindR(Node* root, const K& key) 
{
	if (root == nullptr)
		return false;

	if (root->_key == key)
		return true;

	else if (root->_key < key)
		return _FindR(root->_right, key);
	else
		return _FindR(root->_left, key);

	return false;
}

bool FindR(const K& key) // 递归查找
{
	return _FindR(_root, key);
}

二叉搜索树的难点在于删除
分别有三种情况

  1. 被删的节点没有孩子节点
  2. 被删的节点只有左孩子或右孩子

第一种情况直接删不用特殊处理
第二种情况将被删节点的孩子节点
连接到他的父节点即可

在这里插入图片描述
3. 被删的节点有两个孩子节点

这时被删的节点的父节点
无法接管他的两个孩子节点
解决方法:
请一个节点替自己接管自己的两个孩子
这个节点可以是左子树最大的节点 or
右子树最小节点

删除接口代码实现

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->_left == cur)
					{
						parent->_left = cur->_right;
					}
					else
					{
						parent->_right = cur->_right;
					}
				}
				delete cur;
			}
			// 2. 右为空
			else if (cur->_right == nullptr)
			{
				if (cur == _root) // 解决根节点没有父节点的问题
				{
					_root = _root->_left;
				}
				else
				{
					if (parent->_left == cur)
					{
						parent->_left = cur->_left;
					}
					else
					{
						parent->_right = cur->_left;
					}
				}
				delete cur;
			}
			// 3. 左右都不为空
			else
			{
				// 找右树最小节点替换被删节点,也可以是左树最大节点
				Node* pMinRight = cur;
				Node* MinRight = cur->_right;
				while (MinRight->_left)
				{
					pMinRight = MinRight;
					MinRight = MinRight->_left;
				}

				cur->_key = MinRight->_key;
				if (pMinRight->_left == MinRight)
				{
					pMinRight->_left = MinRight->_right;
				}
				else
				{
					pMinRight->_right = MinRight->_right;
				}

				delete MinRight;
			}
			return true;  
		}
	}

	return false;
}

删除递归实现

bool _EraseR(Node*& root, const K& key)
{
	if (root == nullptr)
		return false;

	if (root->_key < key)
	{
		return _EraseR(root->_right, key);
	}
	else if (root->_key > key)
	{
		return _EraseR(root->_left, key);
	}
	else
	{
		// 删除节点
		Node* del = root; // 保存要删的节点
		if (root->_right == nullptr)
			root = root->_left;
		else if (root->_left == nullptr)
			root = root->_right;
		else
		{
			Node* MaxLeft = root->_left;
			while (MaxLeft->_right)
			{
				MaxLeft = MaxLeft->_right;
			}

			swap(root->_key, MaxLeft->_key);

			return _EraseR(_root->_left, key); // 转换成子树去删除
		}

		delete del;
		return true;
	}
}

✨✨✨✨✨✨✨✨
本篇博客完,感谢阅读🌹
如有错误之处可评论指出
博主会耐心听取每条意见
✨✨✨✨✨✨✨✨

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

新能源汽车与计算机技术:共创智能出行新时代

新能源汽车与计算机技术&#xff1a;共创智能出行新时代 一、引言 新能源汽车以其环保、节能的特性逐渐成为未来出行的趋势&#xff0c;而计算机技术的融入则为新能源汽车带来了前所未有的变革。从电池管理到自动驾驶&#xff0c;再到车联网&#xff0c;计算机技术在新能源汽…

【10】ES6:Promise 对象

一、同步和异步 1、JS 是单线程语言 JavaScript 是一门单线程的语言&#xff0c;因此同一个时间只能做一件事情&#xff0c;这意味着所有任务都需要排队&#xff0c;前一个任务执行完&#xff0c;才会执行下一个任务。但是&#xff0c;如果前一个任务的执行时间很长&#xff…

【JavaEE】多线程(6) -- 定时器的使用及实现

目录 定时器是什么 标准库中的定时器的使用 实现定时器 定时器是什么 Java中的定时器是一种机制&#xff0c;用于在预定时间执行某个任务。它允许开发人员在指定的时间间隔内重复执行任务&#xff0c;或在指定的延迟之后执行任务。定时器是Java提供的一种方便的工具&#xf…

接口测试工具——ApiFox使用初体验 postman导出和ApiFox导入

目录 ApiFox使用初体验初步使用从postman导出到apifox导入 IDEA简单测试Postman测试工具post请求 接口测试工具swaggerKnife4j1.引入依赖2.配置3.常用注解4.接口测试 JMeter什么是JMeter?JMeter安装配置1.官网下载2.下载后解压3.汉语设置 JMeter的使用方法1.新建线程组2.设置参…

【ES】es介绍

倒排索引&#xff08;Inverted Index&#xff09;和正排索引&#xff08;Forward Index&#xff09; 正排索引是一种以文档为单位的索引结构&#xff0c;它将文档中的每个单词或词组与其所在的文档进行映射关系的建立。正排索引通常用于快速检索指定文档的内容&#xff0c;可以…

腾讯云服务器怎么买划算?腾讯云服务器新用户优惠购买攻略

腾讯云轻量应用服务器购买指南&#xff0c;有两个入口&#xff0c;一个是在特价活动上购买&#xff0c;一个是在轻量应用服务器官方页面购买&#xff0c;特价活动上购买价格更便宜&#xff0c;轻量2核2G3M带宽服务器62元一年起&#xff0c;阿腾云atengyun.com分享腾讯云轻量应用…

Java学习——设计模式——创建型模式1

文章目录 创建型模式单例饿汉式懒汉式存在的问题 工厂方法简单工厂模式工厂方法模式抽象工厂模式 创建型模式 关注点是如何创建对象&#xff0c;核心思想是要把对象创建和使用相分离&#xff0c;这样两者能相对独立地变换 包括&#xff1a; 1、工厂方法&#xff1a;Factory Met…

雷军的最后一战,就这?

作者 | 魏启扬 来源 | 洞见新研社 2021年3月30日&#xff0c;小米官宣进军电动汽车赛道后的1003天&#xff0c;小米汽车亮相了。 由于是雷军“人生中最后一次重大的创业项目”&#xff0c;押上了雷军“人生所有积累的战绩和声誉”&#xff0c;小米对于造车极为重视&#xff…

hyper-v ubuntu 3节点 k8s集群搭建

前奏 搭建一主二从的k8s集群&#xff0c;如图所示&#xff0c;准备3台虚拟机。 不会创建的同学&#xff0c;可以看我上上篇博客&#xff1a;https://blog.csdn.net/dawnto/article/details/135086252 和上篇博客&#xff1a;https://blog.csdn.net/dawnto/article/details/135…

6、LLaVA

简介 LLaVA官网 LLaVA使用Vicuna(LLaMA-2)作为LLM f ϕ ( ⋅ ) f_\phi() fϕ​(⋅)&#xff0c;使用预训练的CLIP图像编码器 ViT-L/14 g ( X v ) g(X_v) g(Xv​)。 输入图像 X v X_v Xv​&#xff0c;首先获取feature Z v g ( X v ) Z_vg(X_v) Zv​g(Xv​)。考虑到最后一…

SLF4J: Class path contains multiple SLF4J bindings.解决

背景 项目正常运行几年&#xff0c;近期优化调整修复漏洞&#xff0c;依赖升级后cleaninstall 重启发现项目启动失败&#xff0c;访问所有接口都报错404 错误信息 output输出异常信息截图 tomcat 打印异常信息截图 output打印异常信息详情 D:\javaRuanJian\Tomcat\apach…

【IDEA - EasyCode】好物推荐 -> 代码自动生成工具

目录 一、EasyCode 一、EasyCode 只要是与数据库相关的代码都可以通过自定义模板来生成&#xff0c;支持数据库类型与 java 类型映射关系配置。 使用步骤如下&#xff1a; a&#xff09;下载插件 b&#xff09;准备一张表作为生成元数据&#xff0c;例如如下 user 表 c&…

Android Security PIN 相关代码

开发项目遇到一个问题&#xff0c;具体描述及复制步骤如下&#xff1a; 就是开启"Enhanced PIN privacy"(增强的PIN隐私)的时候输入秘密的时候还是会显示数字 如下图&#xff0c;应该是直接是“.” 不应该出现PIN 密码 想要的效果如下图&#xff1a; 设置的步骤如下图…

Springboot实现登录注册

功能&#xff1a;1、实现用户的登录 2、实现用户的注册以及重名的判断 LoginControl&#xff1a; package com.example.demo.controls;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; imp…

Android下载gradle失败解决方法

1、在gradle-wrapper.properties文件中查看自己需要下载gradle什么版本的包和zip路径&#xff08;wrapper/dists&#xff09;。 2、在setting中查看Gradle的保存路径&#xff0c;如下图&#xff1a;C:/Users/Administrator/.gradle&#xff0c;加上第一步的zip路径得到下载grad…

java itext5 生成PDF并填充数据导出

java itext5 生成PDF并填充数据导出 依赖**文本勾选框****页眉**&#xff0c;**页脚****图片**实际图 主要功能有文本勾选框&#xff0c;页眉&#xff0c;页脚&#xff0c;图片等功能。肯定没有专业软件画的好看&#xff0c;只是一点儿方法。仅供参考。 依赖 <!--pdf-->&…

数据结构学习 Leetcode322 零钱兑换

关键词&#xff1a;动态规划 完全背包 记忆化搜索 一个套路&#xff1a; 01背包&#xff1a;空间优化之后dp【target1】&#xff0c;遍历的时候要逆序遍历完全背包&#xff1a;空间优化之后dp【target1】&#xff0c;遍历的时候要正序遍历 题目&#xff1a; 方法一&#xff…

CodeWhisperer——轻松使用一个超级强大的工具

CodeWhisperer 简介 CodeWhisperer是亚⻢逊云科技出品的一款基于机器学习的通用代码生成器&#xff0c;可实时提供代码建议。 CodeWhisperer有以下几个主要用途&#xff1a; 解决编程问题&#xff0c;提供代码建议&#xff0c;学习编程知识等等&#xff0c;并且CodeWhisper…

LLM(八)| Gemini语言能力深度观察

论文地址&#xff1a;https://simg.baai.ac.cn/paperfile/fc2138ce-cadb-4a36-b9f7-c4000dea3369.pdf 谷歌最近发布的Gemini系列模型是第一个在各种任务与OpenAI GPT系列相媲美的模型。在本文中&#xff0c;作者对Gemini的语言能力做了深入的探索&#xff0c;做出了两方面的贡献…

微信小程序开发系列-06事件

什么是事件 事件是视图层到逻辑层的通讯方式。事件可以将用户的行为反馈到逻辑层进行处理。事件可以绑定在组件上&#xff0c;当达到触发条件时&#xff0c;就会执行逻辑层中对应的事件处理函数。事件对象可以携带额外信息&#xff0c;如 id, dataset, touches。 事件分类 事…