32.C++二叉树进阶1(二叉搜索树)

⭐上篇文章:31.C++多态4(静态多态,动态多态,虚函数表的存储位置)-CSDN博客

⭐本篇代码:c++学习/18.二叉树进阶-二叉搜索树 · 橘子真甜/c++-learning-of-yzc - 码云 - 开源中国 (gitee.com)

⭐标⭐是比较重要的部分

一. 二叉搜索树的概念

        二叉搜索树也称二叉排序树,它有可能是一颗空树。二叉搜索树性质如下:

1 若它有左子树,那么它的左子树所有节点的值小于根节点

2 如它有右子树,那么它的右子树所有节点的值小于根节点

3 它的左右子树也是二叉搜索树

二叉树的代码框架如下:

#pragma once
#include <iostream>
#include <vector>

//二叉树节点
template<class K>
struct BSTreeNode
{
	K _key;
	BSTreeNode* _left;
	BSTreeNode* _right;

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


//二叉树
template<class K>
class BSTree
{
	typedef BSTreeNode<K> Node;
public:
	bool Insert()
	{}

	bool find()
	{}

	bool Delete)
	{}

	//二叉树不允许修改数据
private:
	Node* _root;	//根节点
};

二. 二叉搜索树的插入 

假设我们插入的节点为key

若一棵树为空,则直接插入即可

若不为空:

        按照二叉搜索树的性质,插入该节点即可。

如果某一个节点和key值相同,则直接不插入

若某一个节点比key值大,则应该将key插入到该节点的左子树中

若某一个节点比key值小,则应该将key插入到该节点的右子树中

然后在其左右子树中进行判断,直到子树节点为空,然后插入即可

流程图如下:

插入函数的代码如下:

	bool Insert(const K& key)
	{
		//根节点为空
		if (!root)
		{
			_root = new Node(key);
			return true;
		}

		//不为空,根据性质插入数据
		Node* cur = _root;
		Node* parent = cur;			//最后cur为空,需要一个节点保存其父节点用于连接
		while (cur)
		{
			if (key > cur->_key)	//比当前值大,找其右子树
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (key < cur->_key)	//比当前树小,找其左子树
			{
				parent = cur;
				cur = cur->_left;
			}
			else
				return false;	//有相同节点,插入失败
		}

		//此时cur为空,cur即为插入的数据
		cur = new Node(key);
		if (cur->_key > parent->_key)
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}
		return true;
	}

 三. 二叉搜索树的查找

        二叉搜索树的查找过程和插入过程非常相似。比如在插入的过程中,如果发现值相同,返回true即可,如果为空,则说明没有这个节点,返回false

	bool find(const K& key)
	{
		if (!_root)
			return false;

		Node* cur = _root;
		while (cur)
		{
			if (key > cur->_key)
				cur = cur->_right;
			else if (key < cur->_key)
				cur = cur->_left;
			else 
				return true;
		}
		//cur为空,没有key这个节点
		return false;
	}

 四. 二叉树的删除

        二叉树的删除比较麻烦。可以分为下面四个情况。

1 该节点无左右子节点:

2 该节点只有左子树:

3 该节点只有右子树

4 该节点有左右子树

实际情况1与情况2或者3是重合的,所以只需考虑三者情况 

4.1 删除节点右子树为空

下面这流程图我们要删除节点9

若删除节点是父亲左孩子,父亲的左指向删除节点的左孩子

若删除节点是父亲的右孩子,父亲的右指向删除节点的左孩子

如果一个节点左右都为空,也满足这种情况

4.2 删除节点左子树为空 

        与上面情况类似

若删除节点是父亲左孩子,父亲的左指向删除节点的右孩子

若删除节点是父亲的右孩子,父亲的右指向删除节点的右孩子

4.3 左右孩子均不为空

        此时需要找到一个节点来替代这个节点,在二叉搜索树中,根据其左子树节点比根节点小,右子树节点比根节点大的性质。如果一给节点左右不为空,只需找出其左子树最大,或者右子树最小来替代这个节点即可。

流程图如下:

4.4 删除节点代码 

//删除
	bool Erase(const K& key)
	{
		//1.该节点只有一个孩子
		//2.该节点没有孩子
		//3.该节点有三个孩子
		Node* cur = _root;
		Node* parent = cur;
		while (cur)
		{
			if (cur->_key < key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_key > key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else//找到了,开始删除
			{
				if (cur->_left == nullptr)
				{
					//当cur为根的时候要特判
					if (cur == _root)
					{
						_root = cur->_right;
					}

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

					if (cur == parent->_left)
					{
						parent->_left = cur->_left;
					}
					else
					{
						parent->_right = cur->_left;
					}
					delete cur;
					cur = nullptr;
				}
				else//两边都不为空
				{
					//找到左边最大,右边最小与当前值交互即可
					Node* rightmin = cur->_right;
					Node* rightminparent = cur;
					
					while (rightmin->_left)
					{
						rightminparent = rightmin;
						rightmin = rightmin->_left;
					}
					cur->_key = rightmin->_key;

					//如果cur的右孩子是最小的,直接让cur指向右孩子的右孩子即可
					if (rightmin == cur->_right)
					{
						cur->_right = rightmin->_right;
					}
					else
					{
						rightminparent->_left = rightmin->_right;
					}

					delete rightmin;
					rightmin = nullptr;
				}
				return true;
			}
		}
		return false;
	}

五. 二叉搜索树的遍历 

        根据二叉搜索树的性质,如果我们以中序遍历(左根右),那我们的遍历的时候先是最小的,然后依次遍历更大的。那么最终的排序顺序是有序的!

中序遍历代码如下:

	
	void _InOrder(const Node* root)
	{
		if (!root)
			return;
		//中序遍历
		_InOrder(root->_left);
		cout << root->_key << " ";
		_InOrder(root->_right);
	}

六.代码与测试

 bstree.h

#pragma once
#include<iostream>
#include<vector>
using namespace std;

template<class K>
struct BSTreeNode
{
	BSTreeNode* _left;
	BSTreeNode* _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)
		{
			_root = new Node(key);
			return  true;
		}

		Node* cur = _root;
		Node* parent = cur;
		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->_left = cur;
		}
		else
		{
			parent->_right = cur;
		}
		return true;
	}

	//查找
	bool find(const K& key)
	{
		if (!_root)
			return false;

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

	//删除
	bool Erase(const K& key)
	{
		//1.该节点只有一个孩子
		//2.该节点没有孩子
		//3.该节点有三个孩子
		Node* cur = _root;
		Node* parent = cur;
		while (cur)
		{
			if (cur->_key < key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_key > key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else//找到了,开始删除
			{
				if (cur->_left == nullptr)
				{
					//当cur为根的时候要特判
					if (cur == _root)
					{
						_root = cur->_right;
					}

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

					if (cur == parent->_left)
					{
						parent->_left = cur->_left;
					}
					else
					{
						parent->_right = cur->_left;
					}
					delete cur;
					cur = nullptr;
				}
				else//两边都不为空
				{
					//找到左边最大,右边最小与当前值交互即可
					Node* rightmin = cur->_right;
					Node* rightminparent = cur;
					
					while (rightmin->_left)
					{
						rightminparent = rightmin;
						rightmin = rightmin->_left;
					}
					cur->_key = rightmin->_key;

					//如果cur的右孩子是最小的,直接让cur指向右孩子的右孩子即可
					if (rightmin == cur->_right)
					{
						cur->_right = rightmin->_right;
					}
					else
					{
						rightminparent->_left = rightmin->_right;
					}

					delete rightmin;
					rightmin = nullptr;
				}
				return true;
			}
		}
		return false;
	}

	//二叉搜索树不允许修改,修改后会导致二叉树失效
	//中序遍历
	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}

private:
	Node* _root = nullptr;
	
	void _InOrder(const Node* root)
	{
		if (!root)
			return;
		//中序遍历
		_InOrder(root->_left);
		cout << root->_key << " ";
		_InOrder(root->_right);
	}
};

test.cpp

#define _CRT_SECURE_NO_WARNINGS 1
#include"bstree.h"

void test1()
{
	BSTree<int> tree;

	vector<int> arr;
	for (int i = 0; i < 10; i++)
	{
		int t = rand() % 10000;
		arr.push_back(t);
		tree.Insert(t);
	}

	tree.InOrder();
	for (int i = 0; i <= 9; i++)
	{
		cout << "删除" << arr[i] << "后:";
		tree.Erase(arr[i]);
		tree.InOrder();
	}
}

int main()
{
	srand((unsigned int)time(0));
	test1();
}

可以看到,排序的顺序是有序的

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

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

相关文章

图论基础算法: 二分图的判定(C++)

二分图的基本概念 什么是二分图? 二分图(Bipartite Graph)是指一个图的顶点集可以被分割为两个互不相交的子集 U U U 和 V V V, 并且图中的每一条边都连接 U U U 中的一个顶点和 V V V 中的一个顶点. 换句话说, 二分图中的顶点可以被分成两组, 组内的顶点之间没有边相连…

pyside6学习专栏(九):在PySide6中使用PySide6.QtCharts绘制6种不同的图表的示例代码

PySide6的QtCharts类支持绘制各种型状的图表&#xff0c;如面积区域图、饼状图、折线图、直方图、线条曲线图、离散点图等&#xff0c;下面的代码是采用示例数据绘制这6种图表的示例代码,并可实现动画显示效果&#xff0c;实际使用时参照代码中示例数据的格式将实际数据替换即可…

爬虫逆向实战小记——解决webpack实记

注意&#xff01;&#xff01;&#xff01;&#xff01;某XX网站实例仅作为学习案例&#xff0c;禁止其他个人以及团体做谋利用途&#xff01;&#xff01;&#xff01; aHR0cHM6Ly9wbW9zLnhqLnNnY2MuY29tLmNuOjIwMDgwL3B4Zi1zZXR0bGVtZW50LW91dG5ldHB1Yi8jL3B4Zi1zZXR0bGVtZW5…

Hive-优化(语法优化篇)

列裁剪与分区裁剪 在生产环境中&#xff0c;会面临列很多或者数据量很大时&#xff0c;如果使用select * 或者不指定分区进行全列或者全表扫描时效率很低。Hive在读取数据时&#xff0c;可以只读取查询中所需要的列&#xff0c;忽视其他的列&#xff0c;这样做可以节省读取开销…

【三维生成】StarGen:基于视频扩散模型的可扩展的时空自回归场景生成

标题&#xff1a;《StarGen: A Spatiotemporal Autoregression Framework with Video Diffusion Model for Scalable and Controllable Scene Generation》 项目&#xff1a;https://zju3dv.github.io/StarGen 来源&#xff1a;商汤科技、浙大CAD、Tetras.AI 文章目录 摘要一、…

vue2(笔记)4.0vueRouter.声明式/编程式导航以及跳转传参.重定向

---vueRouter 五个步骤: 两个核心: {path:路径,component:组件} 二级路由: 1.在主页路由对象中,添加children配置项 2.准备路由出口 示例代码: {path: /,component: layout,redirect: home,children: [{path: /home,component: home},{path: /card,component: card}]}, 在l…

内网渗透信息收集linuxkali扫描ip段,收集脚本(web安全)

内网ip段扫描↓ 工具1↓ nmap -sn 192.168.128.0/24工具2↓ nbtscan 192.168.128.0/24 工具↓3 arp-scan -t 1000 192.168.128.0/24 cmd命令扫描↓ for /L %I in (1,1,255) Do ping -w 1 -n 1 192.168.128.%I | findstr "TTL" 这个命令在Windows命令提示符下使…

DeepSeek崛起:如何在云端快速部署你的专属AI助手

在2025年春节的科技盛宴上&#xff0c;DeepSeek因其在AI领域的卓越表现成为焦点&#xff0c;其开源的推理模型DeepSeek-R1擅长处理多种复杂任务&#xff0c;支持多语言处理&#xff0c;并通过搜索引擎获取实时信息。DeepSeek因其先进的自然语言处理技术、广泛的知识库和高性价比…

python-leetcode 48.二叉树的最近公共祖先

题目&#xff1a; 给定一个二叉树&#xff0c;找到该树中两个指定节点的最近公共祖先 百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个节点 p、q&#xff0c;最近公共祖先表示为一个节点 x&#xff0c;满足 x 是 p、q 的祖先且 x 的深度尽可能大&#xff0…

示例:在WPF中如何使用Segoe MDL2 Assets图标和使用该图标的好处

一、目的&#xff1a;分享在WPF中如何使用Segoe MDL2 Assets图标和使用该图标的好处 在WPF中使用Segoe MDL2 Assets字体&#xff0c;可以通过设置控件的FontFamily属性来实现。Segoe MDL2 Assets是一个包含许多图标的字体&#xff0c;通常用于Windows应用程序的图标显示。 二、…

QT——基于 QListWidget 和 QStackedWidget 的页面切换

Qt 练习题&#xff1a;基于 QListWidget 和 QStackedWidget 的页面切换 Qt 练习题&#xff1a;基于 QListWidget 和 QStackedWidget 的页面切换 题目描述&#xff1a; 请使用 Qt 设计一个窗口&#xff0c;其中包含一个 QListWidget 和一个 QStackedWidget。要求实现以下功能&a…

Docker概念与架构

文章目录 概念docker与虚拟机的差异docker的作用docker容器虚拟化 与 传统虚拟机比较 Docker 架构 概念 Docker 是一个开源的应用容器引擎。诞生于 2013 年初&#xff0c;基于 Go 语言实现。Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中&#xf…

linux server docker 拉取镜像速度太慢或者超时的问题处理记录

已经按网上的帖子将镜像地址改为国内的了,用docker info命令查看,如下图所示: 但是还存在下载镜像特别卡的问题,而不是直接报错了,如下图所示: 甚至已经连续下载一晚上了,还是卡在这里,不见任何下载进展。 我在window的docker中下载了对应的镜像,并用以下语句生成了…

四十二:VSCODE打开新文件覆盖上一个文件窗口问题

VSCODE打开新文件覆盖上一个文件窗口问题_vscode enablepreview-CSDN博客

shell文本处理

shell文本处理 一、grep ​ 过滤来自一个文件或标准输入匹配模式内容。除了 grep 外&#xff0c;还有 egrep、fgrep。egrep 是 grep 的扩展&#xff0c;相当于 grep -E。fgrep 相当于 grep -f&#xff0c;用的比较少。 用法 grep [OPTION]... PATTERN [FILE]...支持的正则描述…

下载b站视频音频

文章目录 方案一&#xff1a;jjdown如何使用 方案二&#xff1a;bilibili哔哩哔哩下载助手如何使用进入插件网站插件下载插件安装 使用插件下载视频音频&#xff1a;复制音频下载地址 方案三&#xff1a;bat命令下载单个音频下载单个视频下载单个音视频 方案一&#xff1a;jjdo…

【项目管理】基于 C 语言的 QQ 聊天室实现(TCP + 多线程 + SQLite3)

基于 C 语言的 QQ 聊天室(TCP + 多线程 + SQLite3) 项目功能基础功能: 登录、注册、添加好友、私聊、创建群聊、群聊扩展功能: 删除好友、注销账号、好友在线状态、群管理(拉人/踢人)、VIP 特权、邮件通知等 功能介绍:模拟QQ聊天客户端:登录界面:1、登录2、注册 //将用…

Linux驱动开发-字符设备驱动开发

Linux驱动开发-字符设备驱动开发 一&#xff0c;Linux驱动开发二&#xff0c;字符设备驱动开发1.具体实现2.1.1驱动模块具体函数实现2.1.2 应用调试模块具体函数实现2.1.3 Makefile2.1.4 进行测试2.1.4.1创建节点2.1.4.2 加载和卸载驱动模块2.1.4.3 测试 2.字符设备驱动应用程序…

e2studio开发RA4M2(17)----ADC扫描多通道采样

e2studio开发RA4M2.17--ADC扫描多通道采样 概述视频教学样品申请硬件准备参考程序源码下载ADC属性配置回调函数主程序演示结果 概述 在嵌入式系统中&#xff0c;ADC&#xff08;模数转换器&#xff09;是一个非常重要的组件&#xff0c;它将模拟信号转换为数字信号。为了提高采…

编写一个基于OpenSSL的SSL/TLS服务端(HTTPS)可运行的完整示例

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github&#xff1a;codetoys&#xff0c;所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的&#xff0c;可以在任何平台上使用。 源码指引&#xff1a;github源…