初阶数据结构:二叉搜索树

目录

概念

性能

效率分析

二分缺陷

功能

插入

查找

删除

实现

应用


概念

二叉搜索树(又称:二叉排序树),是由一些具有特别性质的二叉树衍变而来。

只要一棵二叉树具备以下性质,即可称作二叉搜索树。

【1】若左子树不为空,左子树上所有结点的值都小于等于根结点的值

【2】若右子树不为空,右子树上所有结点的值都大于等于根结点的值

【3】根的左右子树也都为二叉搜索树

⼆叉搜索树中可以⽀持插⼊相等的值,也可以不⽀持插⼊相等的值,具体问题具体分析。

C++的STL中的map\set\mutimap\mutiset的底层就是二叉搜索树,muti-支持重复。

性能

就二叉搜索树的性质来看,

一系列值以根节点为中心,比根小的放左边,比根大的放右边,各自分散到根两侧的左右子树。

似乎能品出一丝二分查找的味道?

二分查找也是从中间值开始,分为左右两个区间,小数往左区间找,大数往右区间找。

有些异曲同工之妙。

效率分析

正是本质相同,二叉搜索树与二分查找在查找的功能上可以进行类比,

但二叉搜索树的查找的时间复杂度可以直接看作二分查找的O(log2 N)吗?

最优情况下,二叉搜索树为完全二叉树(或接近完全二叉树)时,其高度为log2 N,时间复杂度可看作O(log2 N)

最坏情况下,二叉搜索树退化为单支树(或接近单支)时,高度为N,时间复杂度就是O(N)了

综合来看,二叉搜索树受结构影响,查找的时间复杂度是O(N)。

可见二叉搜索树如此的效率并不总能达到我们的需求,

所以后续平衡二叉树AVL树和红黑树就是在二叉搜索树的基础上进行变形调整,尽量将二叉搜索树平衡为完全二叉树结构,以保证效率。(后续展开学习)

二分缺陷

那么假如不知道红黑树和AVL树的,稳定log2 N的二分查找是否就一定优于二叉搜索树呢?

二分查找的缺陷:

【1】存储值的容器支持随机访问,且值必须有序

【2】查找后要进行修改,插入、删除的效率低下。因为存在支持随机访问的容器中(vector、deque等),插入删除一般就要挪动数据。

这也就体现了二叉搜索树的进阶--平衡二叉树的价值。

功能

插入

二叉搜索树要插入数据,只要根据其特性进行即可。

具体过程:

树空,第一个插入的结点就是根结点。

树不空,则每次进行比较,插入值小于当前结点,往左走;插入值大于当前结点,往右走。直到找到空位置,插入新结点。

如果要插入相等值,则改动比较逻辑,小于等于往左走,大于往右走(反之也可)。自行抉择相等值放左树还是右数,可不能既要又要。

bool insert(const Key& x)
{Node* node = new Node(x);Node* cur = _root;Node* parent = cur;     //插入新结点,得知道往哪里插if (_root == nullptr){_root = node;return true;}while (cur){if (x > cur->_key){parent = cur;cur = cur->right;}else if (x < cur->_key){parent = cur;cur = cur->left;}else{return false;}}if (x > parent->_key)       //知道插给谁,还要知道 插左or插右{parent->right = node;}else if (x < parent->_key){parent->left = node;}return true;
}

查找

1.查找x,从根开始⽐较,x⽐根值⼤则走右边继续找,x⽐根值⼩则走左边继续找。

2. 最多查找⾼度次,⾛到空,还没找到,这个值不存在。

3. 如果不⽀持插⼊相等的值,找到x即可返回。

4. 如果⽀持插⼊相等的值,意味着有多个x存在,⼀般要求查找中序的第⼀个x。

Node* find(const Key& x)
{Node* cur = _root;while (cur){if (x > cur->_key){cur = cur->right;}else if (x < cur->_key){cur = cur->left;}else{return cur;}}return nullptr;
}

删除

要删除x,首先要找到x,查找逻辑同上。

关键在于怎么删

由于我们有单链表的基础,应该知道仅仅找到被删结点是不够的,还需要有被删结点的Parent结点,才能进行结点删除的操作。所以要定义两个指针cur和parent.

删除结点时会碰到4种情况:(假设被删结点为N)

【1】N为叶子结点,左右为空;

【2】N的左为空,右不为空;

【3】N的左不为空,右为空;

【4】N的左右不为空。

删除方案:

【1】把N结点的⽗亲对应孩⼦指针指向空,直接删除N结点(情况1可以当成2或者3处理,效果是⼀样的)

【2】把N结点的⽗亲对应孩⼦指针指向N的右孩⼦,直接删除N结点

【3】把N结点的⽗亲对应孩⼦指针指向N的左孩⼦,直接删除N结点

【4】⽆法直接删除N结点,因为N的两个孩⼦⽆处安放,只能⽤替换法删除。
找到N左子树的最大值R,或者N右子树的最小值R来替代N。二选一,都能满足结构特性。

替代N的意思就是将N和R的两个结点的值交换,转⽽变成删除R结点,R结点符合情况2或情况3,可以直接删除。

代码部分请看实现 

实现

template<class Key>
struct BSTree_Node
{BSTree_Node(const Key& x = Key()):left(nullptr),right(nullptr),_key(x){}BSTree_Node* left;BSTree_Node* right;Key _key;
};template<class Key>
class BSTree
{typedef BSTree_Node<Key> Node;public:BSTree() = default;        //生成默认构造bool insert(const Key& x){Node* node = new Node(x);Node* cur = _root;Node* parent = cur;if (_root == nullptr){_root = node;return true;}while (cur){if (x > cur->_key){parent = cur;cur = cur->right;}else if (x < cur->_key){parent = cur;cur = cur->left;}else{return false;}}if (x > parent->_key){parent->right = node;}else if (x < parent->_key){parent->left = node;}return true;}Node* find(const Key& x){Node* cur = _root;while (cur){if (x > cur->_key){cur = cur->right;}else if (x < cur->_key){cur = cur->left;}else{return cur;}}return nullptr;}bool erase(const Key& x){Node* cur = _root;Node* parent = nullptr;while (cur){if (x < cur->_key){parent = cur;cur = cur->left;}else if (x > cur->_key){parent = cur;cur = cur->right;}else                        //左右不为空,最难部分,需要好好理清楚{if (cur->left == nullptr){if (parent == nullptr)   //根的父亲为nullptr{_root = cur->right;}else{//if (parent->_left == cur)这样也行if (cur->_key < parent->_key){parent->left = cur->right;}else{parent->right = cur->right;}}delete cur;return true;}else if (cur->right == nullptr){if (parent == nullptr)   //根的父亲为nullptr{_root = cur->left;}else{//if (parent->_left == cur)这样也行if (cur->_key < parent->_key){parent->left = cur->left;}else{parent->right = cur->left;}}delete cur;return true;}else{Node* rightMin_Parent = cur;Node* rightMin = cur->right;while (rightMin->left){rightMin_Parent = rightMin;rightMin = rightMin->left;}swap(cur->_key, rightMin->_key);if (rightMin_Parent->left == rightMin){rightMin_Parent->left = rightMin->right;}else{rightMin_Parent->right = rightMin->right;}delete rightMin;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);}private:Node* _root = nullptr;
};

应用

key搜索场景

只有key作为关键码,结构中只要存key即可,关键码即为需要搜索到的值,搜索场景只需判断key在不在。key的搜索场景实现的⼆叉树搜索树⽀持增删查,但是不⽀持修改,修改key就破坏搜索树结构了。

【1】小区大门车辆栏杆。扫描车牌,检索是否是小区的车,找到了则抬杠放行,反之不抬杆。

【2】检查一篇英文文章单词拼写是否正确,将词库放入搜索树,读取文章中的单词进行搜索。

key/value搜索场景:

key搜索本身的应用不够广,但如果我们在结点中增添一个值,就有很大的操作空间了。

key存搜索数据,val存该数据的一些附加信息。

通过key可快速找到其对应的value。

template<class Key, class Val>
struct BSTree_Node
{BSTree_Node(const Key& x = Key(),const Val& y = Val()):left(nullptr),right(nullptr),_key(x),_val(y){}BSTree_Node* left;BSTree_Node* right;Key _key;Val _val;
};

【1】简单中英互译词典。key存英文,val存中文。搜索英文,将val存的中文一起显示即可。

【2】商场无人停车场\车库。key存车牌,val存入场时间。出场时即可通过车牌找到入场时间,结合退场时间从而算出停车时长,计算停车费用,缴费后抬杆放行。

【3】统计英文文章词频。读取一个词,查找是否存在。

        不存在说明第一次出现设val为1,存在val++。

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

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

相关文章

详解springcloud gateway工作原理、断言、filter、uri、id、全局跨域、globalfilter等以及关键源码实现

1.gateway概念 网关就是当前微服务项目的"统一入口"程序中的网关就是当前微服务项目对外界开放的统一入口所有外界的请求都需要先经过网关才能访问到我们的程序提供了统一入口之后,方便对所有请求进行统一的检查和管理 2. 网关的主要功能 将所有请求统一经过网关网…

C#中的弱引用使用

弱引用&#xff08;Weak Reference&#xff09;是一种特殊的引用类型&#xff0c;它允许你引用一个对象&#xff0c;但不会阻止该对象被垃圾回收器&#xff08;GC&#xff09;回收。弱引用通常用于需要缓存或跟踪对象&#xff0c;但又不希望因保留引用而导致内存泄漏的场景。弱…

spring响应式编程系列:异步生产数据

目录 示例 大致流程 create new MonoCreate subscribe new LambdaMonoSubscriber monoCreate.subscribe accept success onNext 时序图 类图 数据发布者 MonoCreate 数据订阅者 LambdaMonoSubscriber 订阅的消息体 DefaultMonoSink 本篇文章我们来研究如何将…

MCP Python SDK构建的**SQLite浏览器**的完整操作指南

以下是使用MCP Python SDK构建的SQLite浏览器的完整操作指南&#xff1a; 一、环境准备 安装依赖 # 安装MCP SDK及SQLite支持 pip install mcp sqlite3创建测试数据库 sqlite3 test.db <<EOF CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT); IN…

【Python爬虫基础篇】--3.cookie和session

目录 1.cookie 1.1.定义 1.2.参数 1.3.分类 2.session 3.使用cookie登录微博 4.使用session登录 1.cookie 由于http是一个无状态的协议&#xff0c;请求与请求之间无法相互传递或者记录一些信息&#xff0c;cookie和session正是为了解决这个问题而产生。 例子&#xff1…

风车邮箱系统详细使用指南:Windows与Ubuntu双平台解析

风车邮箱系统V1.2使用手册 风车邮箱系统详细使用指南&#xff1a;Windows与Ubuntu双平台解析 前言 在日常网络活动中&#xff0c;我们经常需要一个临时邮箱来注册各类网站或接收验证码&#xff0c;但不想使用自己的真实邮箱。「风车无线邮箱系统」作为一款优秀的临时邮箱工具…

同样的接口用postman/apifox能跑通,用jmeter跑就报错500

之前没用过jmeter,第一次用调试压测脚本遇到了问题 一样的接口用postman能跑通&#xff0c;用jmeter跑就报错500&#xff0c;百度很多文章都说是该接口需要加一个‘内容编码’改成utf-8,我加了还是不行 后来我就想到apifox好像有隐藏的header&#xff0c;然后开始比较apifox的…

1656打印路径-Floyd回溯/图论-链表/数据结构

蓝桥账户中心 1.税收&#xff1a; “城市的税收”&#xff1a;所以是中介点的税收&#xff0c;经过该点后加上 2.路径&#xff1a; 用数组存储前驱节点从而串成链表 pre[ i ][ j ]代表的是从 i 到 j 的最短路径上 j 的前驱节点是什么 那么便可以pre[ i ][ j ]k 把k加入pa…

Eigen矩阵操作类 (Map, Block, 视图类)

1. Map 类&#xff1a;内存映射&#xff08;零拷贝操作&#xff09; 核心功能 将现有的 C/C 数组或缓冲区映射为 Eigen 矩阵/向量&#xff0c;不复制数据&#xff0c;直接操作原内存。 模板参数 cpp Map<Matrix<Scalar, Rows, Cols, Options, MaxRows, MaxCols>&…

多系统安装经验,移动硬盘,ubuntu grub修改/etc/fstab 移动硬盘需要改成nfts格式才能放steam游戏

总结&#xff1a;我硬盘会自动挂载&#xff0c;直接格式化nfts&#xff0c;steam就能装里面了 机械硬盘装系统真的不行&#xff0c;超级慢游戏还跑不了 --------------------------------------------------------------------底下都不用看 笔记本一个系统&#xff0c;移动硬盘…

JFLAP SOFTWARE 编译原理用(自动机绘图)

csdn全是蛆虫&#xff0c;2mb的软件&#xff0c;都在那里搞收费&#xff0c;我就看不惯&#xff0c;我就放出来&#xff0c;那咋了&#xff01;&#xff01;&#xff01; https://pan.baidu.com/s/1IuEfHScynjCCUF5ScF26KA 通过网盘分享的文件&#xff1a;JFLAP7.1.jar 链接: h…

[Windows] Disk Sorter文件分类管理软件 v16.7.18

[Windows] Disk Sorter文件分类管理 链接&#xff1a;https://pan.xunlei.com/s/VOOl0sDntAdHvlMkc7N0ZOD-A1?pwd966n# Disk Sorter是一个功能强大的文件分类管理软件&#xff0c;允许对本地磁盘、网络共享、NAS设备和企业存储系统中的文件进行分类&#xff0c;并且支持生成…

STM32提高篇: 蓝牙通讯

STM32提高篇: 蓝牙通讯 一.蓝牙通讯介绍1.蓝牙技术类型 二.蓝牙协议栈1.蓝牙芯片架构2.BLE低功耗蓝牙协议栈框架 三.ESP32-C3中的蓝牙功能1.广播2.扫描3.通讯 四.发送和接收 一.蓝牙通讯介绍 蓝牙&#xff0c;是一种利用低功率无线电&#xff0c;支持设备短距离通信的无线电技…

6.1.多级缓存架构

目录 一、多级缓存基础与核心概念 缓存的定义与价值 • 缓存的应用场景&#xff08;高并发、低延迟、减轻数据库压力&#xff09; • 多级缓存 vs 单级缓存的优劣对比 多级缓存核心组件 • 本地缓存&#xff08;Caffeine、Guava Cache&#xff09; • 分布式缓存&#xff08;…

MySQL的MVCC【学习笔记】

MVCC 事务的隔离级别分为四种&#xff0c;其中Read Committed和Repeatable Read隔离级别&#xff0c;部分实现就是通过MVCC&#xff08;Multi-Version Concurrency Control&#xff0c;多版本并发控制&#xff09; 版本链 版本链是通过undo日志实现的&#xff0c; 事务每次修改…

基于OpenMV+STM32+OLED与YOLOv11+PaddleOCR的嵌入式车牌识别系统开发笔记

基于OpenMV、STM32与OLED的嵌入式车牌识别系统开发笔记 基于OpenMV、STM32与OLED的嵌入式车牌识别系统开发笔记系统架构全景 一、实物演示二、OpenMV端设计要点1. 硬件配置优化2. 智能帧率控制算法3. 数据传输协议设计 三、PyTorch后端核心实现&#xff1a;YOLOv11与PaddleOCR的…

C#中常见的设计模式

文章目录 引言设计模式的分类创建型模式 (Creational Patterns)1. 单例模式 (Singleton)2. 工厂方法模式 (Factory Method)3. 抽象工厂模式 (Abstract Factory)4. 建造者模式 (Builder) 结构型模式 (Structural Patterns)5. 适配器模式 (Adapter)6. 装饰器模式 (Decorator)7. 外…

Nacos简介—3.Nacos的配置简介

大纲 1.Nacos生产集群Web端口与数据库配置 2.Nacos生产集群的Distro协议核心参数 3.Nacos打通CMDB实现跨机房的就近访问 4.Nacos基于SPI动态扩展机制来获取CMDB的数据 5.基于Nacos SPI机制开发CMDB动态扩展 6.Nacos基于CMDB来实现多机房就近访问 7.Nacos生产集群Prometh…

Jest 快照测试

以下是关于 Jest 快照测试的系统化知识总结,从基础使用到底层原理全面覆盖: 一、快照测试核心原理 1. 工作机制三阶段 #mermaid-svg-GC46t2NBvGv7RF0M {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GC46t2NBvGv…

第十六届蓝桥杯大赛软件赛省赛 C/C++ 大学B组 [京津冀]

由于官方没有公布题目的数据, 所以代码仅供参考 1. 密密摆放 题目链接&#xff1a;P12337 [蓝桥杯 2025 省 AB/Python B 第二场] 密密摆放 - 洛谷 题目描述 小蓝有一个大箱子&#xff0c;内部的长宽高分别是 200、250、240&#xff08;单位&#xff1a;毫米&#xff09;&…