数据结构:二叉搜索树详解

news/2025/1/8 5:30:49/

在这里插入图片描述

二叉搜索树详解

  • 一、二叉搜索树的概念
  • 二、 二叉搜索树的性能分析
    • (一)二叉搜索树的效率
    • (二)数组结构和二叉排序树的比较
  • 三、二叉排序树的插入
    • (一)操作规则
    • (二)代码实现
  • 四、二叉排序树的查找
    • (一)操作规则
    • (二)代码实现
  • 五、二叉排序树的删除
    • (一)操作规则
    • (二)代码实现
  • 六、二叉搜索树key_value使用
  • 七、完整源代码
    • 结束语

一、二叉搜索树的概念

⼆叉搜索树,它或者是⼀棵空树,或者是具有以下性质的⼆叉树:

  • 任意结点左⼦树上所有结点的值都⼩于等于这个结点的值
  • 任意结点右⼦树上所有结点的值都⼤于等于这个结点的值

二叉排序树的中序遍历是按照升序排列的,所以⼜称⼆叉排序树,在搜索的本职下兼职排序的任务

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

二、 二叉搜索树的性能分析

(一)二叉搜索树的效率

在这里插入图片描述

所以综合而言二叉搜索树增删查改时间复杂度为: O(N)

效率不稳定,无法达到要求,后面会继续学习平衡⼆叉搜索树AVL树和红黑树,才能高效适用于我们在内存中存储和搜索数据。

(二)数组结构和二叉排序树的比较

假如我们使用数组进行储存和搜索数据,二分查找也可以实现 O(logN) 级别的查找效率,却有不足

    1. 需要存储在⽀持下标随机访问的结构中,并且有序
    1. 插⼊和删除数据效率很低,因为存储在下标随机访问的结构中,插入和删除数据⼀般需要挪动数据。

二叉排序树拥有兼职排序的功能和树的结构及特有性质,完美的避开了上诉的缺点。

三、二叉排序树的插入

(一)操作规则

    1. 树为空,则直接新增结点,赋值给_root指针
    1. 树不空,按二叉搜索树性质,插⼊值比当前结点大往右走,插⼊值比当前结点小往左走,找到空位置,插入新结点。
    1. 如果支持插入相等的值,插⼊值跟当前结点相等的值可以按照规定往右走,也可以往左走,找到空位置,插入新结点。
      在这里插入图片描述

(二)代码实现

  • 递归版本

这个是之前用C语言实现的二叉排序树的插入,当时觉得设计的已经十分巧妙,现在学了C++,使用了更加可维护,高效率的方式实现,也就是下面的非递归。

#define KEY(n) (n ? n->key : -1)typedef struct Node {int key;struct Node *lchild, *rchild;
} Node;Node *getNewNode(int key) {Node *p = (Node *)malloc(sizeof(Node));p->key = key;p->lchild = p->rchild = NULL;return p;
}Node *insert(Node *root, int key) {if (root == NULL) return getNewNode(key);if (key == root->key) return root;if (key < root->key) root->lchild = insert(root->lchild, key);else root->rchild = insert(root->rchild, key);return root;
}
  • 非递归版本

首先传参使用const&加以修饰,在遍历的时候,需要使用parent来记录一下cur前一个结点的位置。


bool  insert(const K& key)
{if (_root == nullptr) {_root = new Node(key);return true;}Node* parent = nullptr, * 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 (key < parent->_key){parent->_left = cur;}else{parent->_right = cur;}return true;
}

四、二叉排序树的查找

(一)操作规则

    1. 从根开始比较,查找x,x比根的值大则往右边走查找,x比根值小则往左边查找。 最多查找高度次,走到到空,还没找到,这个值不存在。
    1. 如果不⽀持插入相等的值,找到x即可返回,如果⽀持插入相等的值,意味着可能有多个x存在,⼀般要求查找中序的第⼀个x。

(二)代码实现


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

五、二叉排序树的删除

(一)操作规则

查找元素是否在⼆叉搜索树中,如果不存在,则返回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){if (cur == _root){_root = cur->_right;}else{if (cur == parent->_right){parent->_right = cur->_right;}else{parent->_left = cur->_right;}}delete cur;}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;}}delete cur;}else{Node* minRightParent = cur;Node* minRight = cur->_right;while (minRight->_left){minRightParent = minRight;minRight = minRight->_left;}cur->_key = minRight->_key;if (minRightParent->_left == minRight){minRightParent->_left = minRight->_right;}else{minRightParent->_right = minRight->_right;}delete minRight;}return true;}}return false;
}

六、二叉搜索树key_value使用

key_value仍然是二叉搜索树,只是此时除了键值以外还多了一个绑定的值,这个值可以是任何类型。增/删/查还是以key为关键字按照二叉排序树的规则进行,可以通过快速查找到key对应的value,同时达到一种映射关系。key/value的搜索场景实现的二叉树搜索树支持修改value

  • 场景1:简单中英互译字典,树的结构中(结点)存储key(英文)和vlaue(中文),搜索时输⼊英⽂,则同时查找到了英文对应的中文。
  • 场景2:商场无人值守⻋库,入口进场时扫描⻋牌,记录⻋牌和入场时间,出口离场时,扫描⻋牌,查找⼊场时间,当前时间-入场时间计算出停车时长,计算出停车费用,缴费后抬杆,车辆离场。
  • 场景3:统计⼀篇⽂章中单词出现的次数,读取⼀个单词,查找单词是否存在,不存在这个说明第⼀次出现,(单词,1),单词存在,则++单词对应的次数。

七、完整源代码

#pragma once#include<iostream>
using namespace std;namespace key {template<class K>struct BSTNode{K _key;BSTNode<K>* _left;BSTNode<K>* _right;BSTNode(const K& key):_key(key),_left(nullptr),_right(nullptr){}};template<class K>class BSTree {typedef BSTNode<K> Node;public:bool  insert(const K& key){if (_root == nullptr) {_root = new Node(key);return true;}Node* parent = nullptr, *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 (key < parent->_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){if (cur == _root){_root = cur->_right;}else{if (cur == parent->_right){parent->_right = cur->_right;}else{parent->_left = cur->_right;}}delete cur;}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;}}delete cur;}else{Node* minRightParent = cur;Node* minRight = cur->_right;while (minRight->_left){minRightParent = minRight;minRight = minRight->_left;}cur->_key = minRight->_key;if (minRightParent->_left == minRight){minRightParent->_left = minRight->_right;}else{minRightParent->_right = minRight->_right;}delete minRight;}return true;}}return false;}void InOrder(){_InOrder(_root);cout << endl;}private:void _InOrder(Node* root){if (root == nullptr) {return;}_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);}Node* _root = nullptr;};}namespace key_value {template<class K, class V>struct BSTNode{K _key;V _value;BSTNode<K, V>* _left;BSTNode<K, V>* _right;BSTNode(const K& key, const V& value):_key(key),_value(value), _left(nullptr), _right(nullptr){}};template<class K, class V>class BSTree {typedef BSTNode<K, V> Node;public:bool  insert(const K& key, const V& value){if (_root == nullptr) {_root = new Node(key, value);return true;}Node* parent = nullptr, * 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, value);if (key < parent->_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){if (cur == _root){_root = cur->_right;}else{if (cur == parent->_right){parent->_right = cur->_right;}else{parent->_left = cur->_right;}}delete cur;}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;}}delete cur;}else{Node* minRightParent = cur;Node* minRight = cur->_right;while (minRight->_left){minRightParent = minRight;minRight = minRight->_left;}cur->_key = minRight->_key;if (minRightParent->_left == minRight){minRightParent->_left = minRight->_right;}else{minRightParent->_right = minRight->_right;}delete minRight;}return true;}}return false;}void InOrder(){_InOrder(_root);cout << endl;}private:void _InOrder(Node* root){if (root == nullptr) {return;}_InOrder(root->_left);cout << root->_key << " " << root->_value << endl;_InOrder(root->_right);}Node* _root = nullptr;};
}

结束语

这篇文章先写到这里,感谢点赞,感谢关注!


http://www.ppmy.cn/news/1561190.html

相关文章

算法电子书合集(pdf)

算法&#xff08;c、java&#xff09;电子书 &#xff08;使用手机保存资料可以获取1T免费网盘空间&#xff09; 资源链接&#xff1a;https://pan.quark.cn/s/25fd93f4176e

密码学文献引用:CryptoBib + DBLP

网络安全和密码学&#xff0c;推荐文献引用&#xff1a; CryptoBib (ens.fr) DBLP: computer science bibliography 文章目录 如何下载如何使用 如何下载 git init# 下载 crypto.bib git submodule add https://github.com/cryptobib/export cryptobib # 或者 crypto_crossr…

华为 Sensor 省电策略调研

华为EMUI 9.0.0.187&#xff08;C00E57R1P15&#xff09; 无该功能 华为EMUI 9.1.0.321&#xff08;C00E320R1P1&#xff09; 之后有sensor管控 一、华为 Sensor 省电策略 1. Sensor 类别只配置非唤醒类Sensor 2. 手机静止情况&#xff0c;应用不可见时达到1分钟&#xff0…

SQL-Server链接服务器访问Oracle数据

SQL Server 链接服务器访问 Oracle 离线安装 .NET Framework 3.5 方法一&#xff1a;使用 NetFx3.cab 文件 下载 NetFx3.cab 文件&#xff0c;并将其放置在 Windows 10 系统盘的 C:Windows 文件夹中。 以管理员身份运行命令提示符&#xff0c;输入以下命令并回车&#xff1a; …

简历_熟悉缓存高并发场景处理方法,如缓存穿透、缓存击穿、缓存雪崩

系列博客目录 文章目录 系列博客目录1.缓存穿透总结 2.缓存雪崩3.缓存击穿代码总结 1.缓存穿透 缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在&#xff0c;这样缓存永远不会生效&#xff0c;这些请求都会打到数据库。 常见的解决方案有两种&#xff1a; 缓存空对…

安徽省地图arcgis数据美化后mxd文件shp格式下载后内容测评

标题中的“安徽省地图arcgis数据美化后mxd文件shp格式”揭示了这个压缩包的内容是经过GIS处理的、针对安徽省地图数据。ArcGIS是一款由Esri公司开发的专业地理信息系统软件&#xff0c;用于处理、分析和展示地理空间数据。MXD文件是ArcGIS的项目文件&#xff0c;包含了地图布局…

RabbitMQ案例

1. 导入依赖 <!--AMQP依赖&#xff0c;包含RabbitMQ--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId></dependency> 发送消息 注入RabbitTemplate Autowired RabbitT…

MySQL 3主集群搭建

由于工作需要&#xff0c;搭建了一套MySQL3主集群。集群使用了MySQL 8.0&#xff0c;在3台Redhat 8.10上完成的搭建。过程如下&#xff1a; 直接官网下载mysql数据库 上传到服务器&#xff0c;解压然后执行 yum localinstall *.rpm 修改数据库配置文件/etc/my.cnf&#xff1a…