二叉搜索树(二叉排序树)

news2024/11/15 23:04:01

文章目录

  • 基本概念
  • 基本操作
  • 实现分析
    • 插入数据
    • 查找数据
    • 删除数据
    • 遍历数据
  • 源码

基本概念

二叉搜索树也叫搜索二叉树、二叉排序树、排序二叉树。是一种对查找和排序都有用的特殊二叉树。
二叉搜索树(Binary Search Tree,简称BST)

如何构建一颗二叉搜索树

假设我们有如下数据,我们按从左往右的顺序构建一颗二叉搜索树

在这里插入图片描述

在这里插入图片描述
1.首先,将8作为根节点
2.插入3,由于3小于8,作为8的左子树
3.插入1,由于1小于8,进入左子树3,1又小于3,则1为3的左子树
4.插入10,由于10大于8,则作为8的右子树
5.插入6,由于6小于8,进入左子树3,6又大于3,则6为3的右子树
6.插入4,由于4小于8,进入左子树3,4又大于3,进入右子树6,4还小于6,则4为6的左子树
7.插入7,由于7小于8,进入左子树3,7又大于3,进入右子树6,7还大于于6,则7为6的右子树
8.插入14,由于14大于8,进入右子树10,14又大于10,则14为10的右子树
9.插入13,由于13大于8,进入右子树10,又13大于10,进入右子树14,13小于14,则13为14的左子树

构建一颗二叉搜索树
①只要左子树为空,就把小于父节点的数插入作为左子树
②只要右子树为空,就把大于父节点的数插入作为右子树
③如果不为空,就一直往下去搜索,直到找到合适的插入位置

基本操作

二叉搜索树,又名二叉排序数,是因为这颗二叉树在使用中序遍历来遍历这颗二叉树的时候数据呈现升序,所以我们使用中序遍历来遍历二叉树

插入数据
在这里插入图片描述

删除数据

在这里插入图片描述

查找数据

在这里插入图片描述

查找元素

实现分析

插入数据

构造一颗二叉搜索树的过程就是插入过程,找寻要插入元素的位置的规则,将要插入的数据与根节点比较,如果比根节点大,则在右子树中寻找,再将要插入的元素跟右子树的根节点比较,如果大,则继续在右子树中找,反之。以此类推,一直找到空节点,如果要插入的元素比根节点小,就将要插入的元素与根节点的左子树比较,同上。
在这里插入图片描述

bool Insert(const K& key)
{
	//判断特殊情况,如果根节点为空,则将要插入的元素作为根节点
	if (_root == nullptr)
	{
		_root = new Node(key);
		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->_left = cur;
	else parent->_right = cur;
	return true;
}

查找数据

它既然也叫二叉搜索树,查找数据非常的便利。它的操作并不是把中序遍历的结果存入数组,然后在有序数组里查找,而是直接在树上查找。其操作与二分查找非常相似,我们来查找7试一试?(这里要说明以下:在正常的数据结构中,由于数据量很大,所以我们也不知道我们想要的元素在不在里面;同时也不知道每个元素具体是多少,只知道他们的大小关系。我们是在此基础上进行查找)
在这里插入图片描述

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

搜索二叉树的查找数据也不是很快,查找一个元素的最快时间复杂度是O(logn)(图左),最坏时间复杂度是O(n)(图右)
在这里插入图片描述

删除数据

二叉树的删除要复杂一点,需要分类讨论分三种情况

情况一 删除叶子节点

在这里插入图片描述

情况二: 要删除的节点至少一个孩子为空

在这里插入图片描述
这两种情况可以写代码时可归为一类,就是将删除节点的子节点托孤给父亲节点

情况三:要删除的节点左右孩子都存在
在这里插入图片描述

	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 {
				//情况一:要删除的节点的左子树为空
				if (cur->_left == nullptr)
				{
					//特殊情况,cur是根节点
					if (cur == _root)
					{
						_root = cur->_right;
					}
					else {
						if (cur == parent->_left)
						{
							parent->_left = cur->_right;
						}
						else if (cur == parent->_right)
						{
							parent->_right = cur->_right;
						}
					}
				}  //情况一:要删除的节点的右子树为空
				else if (cur->_right == nullptr) 
				{
					//特殊情况,cur是根节点
					if (cur == _root)
					{
						_root = cur->_left;
					}
					else {
						if (cur == parent->_left)
						{
							parent->_left = cur->_left;
						}
						else if (cur == parent->_right)
						{
							parent->_right = cur->_left;
						}
					}
				}
				else {
					//左右子树都不为空
					Node* parent = cur; //为防止交换后找不到该节点,所以要一起找到他的父节点
					//找到左子树最大的值,也就是左子树最右边的值
					Node* leftMax = cur->_left;
					while(leftMax->_right)
					{
						parent = leftMax;
						leftMax = leftMax->_right;
					}
					//将找到的适合做点的哪个值和要删除的值交换
					swap(cur->_key, leftMax->_key);
					//两种情况,找到的leftMax是parent的左孩子还是右孩子
					if (leftMax == parent->_left)
					{
						//因为找的是最右边节点,所以他的右孩子为空,直接托孤
						parent->_left = leftMax->_left;
					}
					else {
						parent->_right = leftMax->_left;
					}
					cur = leftMax;//删除交换后的节点
				}
				delete cur;
				return true;
			}
		}
		//找完也没找到
		return false;
	}

遍历数据

二叉搜索树的一个特性,一个节点的所有左子树的值一定比这个节点的值小,一个节点的所有右子树的值比这个节点的值大,所以我们使用中序遍历可以得到一个升序的数组,我们实现的遍历也是利用递归实现中序遍历。
因为调用遍历需要传入根节点,而根节点数据是private类型的,不对外公开,所以我们可以再次封装一层函数用来调用遍历函数。

void InOrder()
{
	_InOrder(_root);
	cout << endl;
}
void _InOrder(Node* root)
{
	if (root == nullptr)return;

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

源码

#pragma once
#include<iostream>

using namespace std;

template<class K>
//二叉搜索树的节点
struct BSTreeNode
{
	struct BSTreeNode* _left;
	struct BSTreeNode* _right;
	K _key;
	
	BSTreeNode(K key = K())
		:_key(key)
		,_left(nullptr)
		,_right(nullptr)
	{}
};

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

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

	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 {
				//左为空
				if (cur->_left == nullptr)
				{
					//特殊情况,cur是根节点
					if (cur == _root)
					{
						_root = cur->_right;
					}
					else {
						if (cur == parent->_left)
						{
							parent->_left = cur->_right;
						}
						else if (cur == parent->_right)
						{
							parent->_right = cur->_right;
						}
					}
				}
				else if (cur->_right == nullptr)  //右为空
				{
					//特殊情况,cur是根节点
					if (cur == _root)
					{
						_root = cur->_left;
					}
					else {
						if (cur == parent->_left)
						{
							parent->_left = cur->_left;
						}
						else if (cur == parent->_right)
						{
							parent->_right = cur->_left;
						}
					}
				}
				else {
					//左右子树都不为空
					Node* parent = cur; //为防止交换后找不到该节点,所以要一起找到他的父节点
					//找到左子树最大的值,也就是左子树最右边的值
					Node* leftMax = cur->_left;
					while(leftMax->_right)
					{
						parent = leftMax;
						leftMax = leftMax->_right;
					}
					//将找到的适合做点的哪个值和要删除的值交换
					swap(cur->_key, leftMax->_key);
					//两种情况,找到的leftMax是parent的左孩子还是右孩子
					if (leftMax == parent->_left)
					{
						//因为找的是最右边节点,所以他的右孩子为空,直接托孤
						parent->_left = leftMax->_left;
					}
					else {
						parent->_right = leftMax->_left;
					}
					cur = leftMax;//删除交换后的节点
				}
				delete cur;
				return true;
			}
		}
		//找完也没找到
		return false;
	}
	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}
	void _InOrder(Node* root)
	{
		if (root == nullptr)return;

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


private:
	Node* _root=nullptr;
};

void TestBSTree1()
{
	int a[] = { 8,3,1,10,6,4,7,14,13 };
	BSTree<int> t; //定义
	for (auto e : a)
	{
		//将a数组的数据依次插入到二叉搜索树中
		t.Insert(e);
	}
	//遍历 - 使用中序遍历,排出升序
	//t.InOrder();

	cout << t.Find(3) << endl; //查找已存在数据
	cout << t.Find(2) << endl; //查找未存数据
}

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

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

相关文章

【使用维纳滤波进行信号分离】基于维纳-霍普夫方程的信号分离或去噪维纳滤波器估计(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

【Windows】WDS中如何跳过语言选择以及身份验证

WDS&#xff08;Windows Deployment Services&#xff09;是微软的一项网络服务&#xff0c;用于快速和方便地部署Windows操作系统到多台计算机上。它提供了一种自动化的方式来安装、配置和管理操作系统映像&#xff0c;使企业能够快速部署和更新大量的计算机系统。网上有很多W…

二叉搜索树的本质

引言 打算写写树形数据结构&#xff1a;二叉查找树、红黑树、跳表和 B 树。这些数据结构都是为了解决同一个基本问题&#xff1a;如何快速地对一个大集合执行增删改查。 本篇是第一篇&#xff0c;讲讲搜索树的基础&#xff1a;二叉搜索树。 基本问题 如何在一千万个手机号中…

设计模式-中介者模式在Java中使用示例-客户信息管理

场景 欲开发客户信息管理窗口界面&#xff0c;界面组件之间存在较为复杂的交互关系&#xff1a;如果删除一个客户&#xff0c; 要在客户列表(List)中删掉对应的项&#xff0c;客户选择组合框(ComboBox)中客户名称也将减少一个&#xff1b; 如果增加一个客户信息&#xff0c;…

SpringBoot2.7集成Swagger3.0和knife4j实现API接口文档开发

1. 概述 Swagger 3 是一个用于描述、构建和测试 RESTful Web 服务的开源工具集。它提供了一种简单而强大的方式来定义和文档化 API 接口&#xff0c;同时还具备自动生成客户端代码和服务器存根代码的功能。 Knife4j是一个集Swagger2 和 OpenAPI3为一体的增强解决方案&#xff…

DNSPod十问秦勇:为什么医疗AI最爱眼科?

本期嘉宾 秦勇 鹰瞳科技Airdoc COO 秦勇&#xff0c;鹰瞳科技&#xff08;Airdoc&#xff09;COO&#xff0c;中国人工智能学会智慧医疗专委会委员&#xff0c;长期从事医疗人工智能方向的工作&#xff0c;拥有丰厚的项目经验、管理经验和技术积累&#xff0c;参与多个医院人…

掌握Python的X篇_13+14_Python条件语句实例:判断闰年、成绩评定

前面学习了条件语句以及调试的基本技巧&#xff0c;本篇介绍两个与条件语句有关的实例&#xff0c;对前面的知识又深刻认识。 文章目录 1. 判断闰年1.1 版本11.2 版本21.3 一行代码太长的处理方法 2. 根据成绩评级 1. 判断闰年 用户输入年份&#xff0c;判断该年份是否为闰年…

基于springboot+jpa+mysql+html网上中药商城系统

基于springbootjpamysqlhtml网上中药商城系统 一、系统介绍二、功能展示1.主页(客户)2.登陆&#xff08;客户&#xff09;3.注册&#xff08;客户&#xff09;4.购物车(客户)5.我的订单&#xff08;客户&#xff09;6.用户管理&#xff08;管理员&#xff09;7.分类管理&#x…

基于鲸鱼优化算法的5G信道估计(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️❤️&#x1f4a5;&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑…

1400*D. Divide by three, multiply by two

题意&#xff1a; 一个序列&#xff0c;每个数都是前一个数乘2或者除3&#xff0c;打乱之后&#xff0c;需要你排出原来的序列。 queue和deque模拟 #include<bits/stdc.h> using namespace std; typedef long long ll; const int N1e55; ll n,a[N]; queue<ll>q; d…

Python进行数据分析(详细教程)

1.为什么选择Python进行数据分析&#xff1f; Python是一门动态的、面向对象的脚本语言&#xff0c;同时也是一门简约&#xff0c;通俗易懂的编程语言。Python入门简单&#xff0c;代码可读性强&#xff0c;一段好的Python代码&#xff0c;阅读起来像是在读一篇外语文章。Pyth…

【Linux命令200例】locate强大的文件查找利器

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;全栈领域新星创作者✌&#xff0c;2023年6月csdn上海赛道top4。 &#x1f3c6;本文已收录于专栏&#xff1a;Linux命令大全。 &#x1f3c6;本专栏我们会通过具体的系统的命令讲解加上鲜活的实操案例对各个命令进行深入…

2.uni-app项目文件

uni-app像是vue与微信小程序的合体&#xff0c;使用 uni-ui项目 模板创建的项目文件如下 目录 1 pages 2 pages.json 3 App.vue 4 index.html 5 static 6 uni_modules 7 manifest.json 8 main.js 9 uni.scss 1 pages 这个是放页面的&#xff0c;默认里面有…

list与sort()

运行代码&#xff1a; //list与sort() #include"std_lib_facilities.h" //声明Item类 struct Item {string name;int iid;double value;Item():name(" "),iid(0),value(0.0){}Item(string ss,int ii,double vv):name(ss),iid(ii),value(vv){}friend istre…

七、用户画像

目录 7.1 什么是用户画像7.2 标签系统7.2.1 标签分类方式7.2.2 多渠道获取标签 7.3 用户画像数据特征7.3.1 常见的数据形式7.3.2 文本挖掘算法7.3.3 嵌入式表示7.3.4 相似度计算方法 7.4 用户画像应用 因此只基于某个层面的数据便可以产生部分个体面像&#xff0c;可用于从特定…

pso优化bp网络机械故障诊断(MATLAB代码)

在混合算法中,需要优化的对象(粒子)是 BP 神经网络的权值和值。首先应把要优化的神经网络的全部权值和闽值构成一个实数数组,并赋予它们 [0,1之间的随机数。然后,按照选定的网络结构,用前向算法计算出对应于每组输入样本的神经网络输出。这里BP网络的激活函数都选为sigmoid 函数…

【unity细节】怎么让物体产生碰撞后不会被撞飞,但是有碰撞停止的效果

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 秩沅 原创 收录于专栏&#xff1a;unity细节和bug ⭐怎么让物体产生碰撞后不会被撞飞&#xff0c;但是有碰撞停止的效果⭐ 文章目录…

PHP-Mysql图书管理系统--【白嫖项目】

强撸项目系列总目录在000集 PHP要怎么学–【思维导图知识范围】 文章目录 本系列校训本项目使用技术 首页phpStudy 设置导数据库后台的管理界面数据库表结构项目目录如图&#xff1a;代码部分&#xff1a;主页的head 配套资源作业&#xff1a; 本系列校训 用免费公开视频&am…

Linux安装Kafka图形化界面

Apache Kafka本身是一个分布式的消息系统&#xff0c;它并没有官方提供的图形界面。Kafka主要通过命令行界面进行管理和操作。 如果你需要一个可视化的界面来管理Kafka&#xff0c;可以考虑安装一些第三方的Kafka管理工具&#xff0c;例如Kafdrop、Kafka Manager或Conduktor等…

NOAUTH Authentication required解决方法

执行redis报错如下&#xff1a; 提示需要进行权限认证 解决方案 输入redis密码 格式&#xff1a; auth 密码