当前位置: 首页 > news >正文

C++--二叉搜索树初阶

       前言:二叉搜索树是一种常用的数据结构,支持快速的查找、插入、删除操作,C++中map和set的特性也是以二叉搜索树作为铺垫来实现的,而二叉搜索树也是一种树形结构,所以,在学习map和set之前,我们先来学习这种新的树形结构--二叉搜索树。

目录

1.二叉搜索树

二叉搜索树的功能及其实现

二叉搜索树的插入和查找

二叉搜索树的删除

查找函数递归实现

插入函数递归实现

删除函数递归实现

拷贝构造和赋值运算符重载

搜索二叉树的相关性质及其应用

二叉搜索树的查找

key-value模型


1.二叉搜索树

二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:

     若它的左子树不为空,则左子树上所有节点的值都小于根节点的值

     若它的右子树不为空,则右子树上所有节点的值都大于根节点的值

     它的左右子树也分别为二叉搜索树,同样的,我们可以发现,二叉搜索树的中序遍历是升序排序的

二叉搜索树的功能及其实现

        我们先定义基础的节点的数据结构,明显是一个二叉树类型的节点,有左右节点,运用模板知识,我们可以描述出以下的节点结构体和搜索树的类的描述:

template<class T>
struct BSTreeNdoe {BSTreeNdoe(const T& data = T()): _left(nullptr), _right(nullptr), data(data){}BSTreeNdoe<T>* _left;BSTreeNdoe<T>* _right;T data;};
template<class T>
class BSTree {typedef BSTreeNdoe<T> Node;
public://成员函数private:Node* root = nullptr;};

下面的功能函数将从上面给出的结构来进行扩充功能和优化改写:

二叉搜索树的插入和查找

插入的具体过程如下

a. 树为空,则直接新增节点,赋值给root指针

b. 树不空,按二叉搜索树性质查找插入位置,插入新节点,从树的根节点依次寻找:如果待插入的值比当前节点大,那直接插入当前节点的右边,反之,插入当前节点的左边,这里注意,一般的,在二叉搜索树中不会存在相同的值,需要注意的是,为了保证新节点能够和树连接起来,需要记录好父节点

//插入操作bool insert(const T& key){if (root == nullptr)//如果当前树为空,那么直接建树{root = new Node(key);return true;}//如果不为空,直接寻找合适的位置插入即可Node* cur = root;Node* parent = nullptr;//记录父亲节点用来连接while (cur){parent = cur;if (cur->data > key)//比当前节点小,往当前节点左边走cur = cur->_left;else if (cur->data < key)//比当前节点大,往当前节点右边走cur = cur->_right;//一般来说,搜索二叉树不会存在相同的两个值elsereturn false;}cur = new Node(key);if (parent->data > key)parent->_left = cur;else if (parent->data < key)parent->_right = cur;return true;}

二叉搜索树的查找

a、从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找。

b、最多查找高度次,走到到空,还没找到,这个值不存在。

bool find(const T& key){Node* cur = root;while (cur){if (cur->data > key)cur = cur->_left;else if (cur->data < key)cur = cur->_right;elsereturn true;}return false;}

二叉搜索树的删除

        首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情况:

a. 要删除的结点无孩子结点

b. 要删除的结点只有左孩子结点

c. 要删除的结点只有右孩子结点

d. 要删除的结点有左、右孩子结点

       实际上,这四种情况中,a情况可以算作b或者c情况中,因为删除叶子结点,本质上可以看做要删除的节点只有为nullptr的右孩子或者左孩子节点,我们来分别对这三种情况进行分析,

       对于替换法,我们需要注意特殊的情况,从上面的图可以看出,左子树中的最右节点,本质上就是第一次往待删除节点的左孩子节点走,之后的每一步,都开始往其右孩子方向走,右子树的最左节点是第一次往待删除节点的右孩子节点走,之后一直往其左孩子方向走,此时,我们的替换法是建立在待删除节点的左右孩子都存在的前提下,所以,左树最右节点和右树最左节点,左树和右树一定都具备,但是不一定有最右和最左节点,下面我们就以左树最右节点为例,来进行讨论分析:

下面是一颗普通的二叉树:

        因为此时待删除节点需要满足存在左右节点,所以,此时符合要求的节点只有8,3,其中8是正常的情况,因为其左子树有右孩子,通过8到3之后可以直接找到7,所以8可以直接和7进行交换,因为此时已经遍历到了左子树的最右节点,也就是说,该节点不可能再存在右孩子,但是不能保证没有左孩子,比如说没有7这个节点,要删除8,8就只能和6交换,此时我们需要将4,也就是被交换节点的左子树放在3的右孩子上,这样就能达到删除8的目的;

     此时我们再来看3这个节点,这个节点特殊在其只有一个左孩子节点,并且左孩子节点没有了右子树,导致我们找左树最右节点的时候,只能找到第一步左树的位置而被迫停下来,此时,这个左树就只能当做左树的最右节点来看待,只是此时我们在将1和3交换之后,操作上要发生变化,因为此时没有了右子树,所以被交换后,3成了左子树,也就是说需要删除的是1的左子树而不是像上面的情况一样直接将被交换节点的父节点的右树直接被删除节点的左树。

这里我们给出代码和测试二叉树,方便我们下来模拟运行和理解:

//删除节点bool Erase(const T& key){Node* parent = nullptr;Node* cur = root;while (cur){if (cur->data < key){parent = cur;cur = cur->_right;}else if (cur->data > key){parent = cur;cur = cur->_left;}else{//找到了,开始删除//只有右孩子if (cur->_left == nullptr){//此时父节点有三种情况//如果要删除的是根节点,需要特殊处理if (cur == root)root =cur->_right;else if (cur == parent->_left)parent->_left = cur->_right;else if (cur == parent->_right)parent->_right = 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 if (cur == parent->_right)parent->_right = cur->_left;delete cur;}//有左右两个孩子节点else{//法1:假设默认选择左子树的最大节点//Node* pright = cur->_left;//先找到左子树,此时我们能确保待删除节点是有两个孩子的,所以p一定不为空//Node* pp = cur;//初始时父节点等于待删除节点//while (pright->_right)//{//	pp = pright;//更新父节点//	pright = pright->_right;//}//std::swap(cur->data, pright->data);////if (pright == pp->_left)//左子树不存在右孩子,直接将p->_left赋值pp的左孩子即可//	pp->_left = pright->_left;//else  //左子树存在右孩子//	pp->_right = pright->_left; //直接将交换后的节点的左子树放到待删除节点的父节点的右子树上//delete pright;//法2:假设选择右子树的最小节点Node* pleft = cur->_right;//选择右子树,但是最终目的是寻找右子树的最左节点Node* pp = cur;while (pleft->_left){pp = pleft;pleft = pleft->_left;}std::swap(cur->data, pleft->data);if (pleft == pp->_right)//如果待删除节点的右子树没有左孩子pp->_right = pleft->_right;else //如果是正常情况pp->_left = pleft->_right;//最左节点的右孩子需要保留在pp的左子树delete pleft;}return true;}}return false;}

查找函数递归实现

protected:bool findr(Node* root,const T&key){if (root == nullptr)return false;else if (root->data > key)return finr(root->_left, key);else if (root->data < key)return finr(root->_right, key);elsereturn true;}
public:bool FindR(const T&key){return findr(root,key);}

插入函数递归实现

protected:bool insertr(Node* &root, const T& key)//由于要修改原树,所以建议传引用{if (root == nullptr){root = new Node(key);return true;}if (root->data > key)return insertr(root->_left, key);else if (root->data < key)return insertr(root->_right, key);elsereturn false;}
public:bool InsertR(const T& key){return insertr(root, key);}

删除函数递归实现

//节点的删除递归实现
protected:bool eraser(Node* &root, const T& key){if (root == nullptr)return false;if (root->data < key){return eraser(root->_right, key);}else if (root->data > key){return eraser(root->_left, key);}else{// 删除if (root->_left == nullptr){Node* del = root;root = root->_right;delete del;return true;}else if (root->_right == nullptr){Node* del = root;root = root->_left;delete del;return true;}else{//以删除右子树的最小节点为例Node* pleft = root->_right;while (pleft->_left){pleft = pleft->_left;}std::swap(root->data, pleft->data);// 转换成在子树递归删除return eraser(root->_right, key);}}}
public:bool EraseR(const T& key){return eraser(root, key);}

拷贝构造和赋值运算符重载

	//拷贝构造函数
protected:Node* copy(Node* root){if (root == nullptr)return nullptr;Node* newroot = new Node(root->data);newroot->_left = copy(root->_left);newroot->_right = copy(root->_right);return newroot;}
public:BSTree(const BSTree<T>& b){root = copy(b.root);}BSTree(){}//构造函数//赋值运算符重载BSTree<T>& operator=(BSTree<T>& b){std::swap(root, b.root);return *this;}

搜索二叉树的相关性质及其应用

二叉搜索树的查找

       我们不难发现,搜索二叉树的中序遍历是对应的升序排列,当我们在二叉搜索树中查找某个值的时候,我们却不能简单的认为其查找效率是logn级别的,因为查找效率需要考虑最坏的情况,而我们的logn的效率实际上是理想化的情况,比某个数大和比某个数小的数各占一半,分别在这个数的左右两侧子树上,才有logn级别的效率,但是,若是在最坏的情况下,搜索树变成了类似于单链表式的链状结构,则查找效率就会变成 o(n) 级别的,因此,我们需要对二叉搜索树进行优化才能符合我们的要求,就叫做所谓的平衡搜索二叉树(AVL树,红黑树等),具体我们后面再做讲解。

key-value模型

        实际上,在C++中,key-value模型的例子有很多,比如map,pair等数据结构,它们通常是一个键值,一个值的方式组成,其中,键值作为功能函数的主要参数,相当于数组的下标,有了下标,我们就可以按下标访问任何一个存在的元素,相当于查找等操作,现在,我们想在二叉搜索树中实现key-value模型,此时,我们只需要将本来一个节点存储的一个数据,修改为一个节点可以存储两个数据即可,具体细节见下面的实现:

namespace key_value{template<class K, class V>struct BSTreeNode{BSTreeNode<K, V>* _left;BSTreeNode<K, V>* _right;K _key;V _value;BSTreeNode(const K& key, const V& value):_left(nullptr), _right(nullptr), _key(key), _value(value){}};template<class K, class V>class BSTree{typedef BSTreeNode<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;Node* cur = _root;while (cur){parent = cur;if (cur->_key < key){cur = cur->_right;}else if (cur->_key > key){cur = cur->_left;}else{return false;}}cur = new Node(key, value);if (parent->_key < key){parent->_right = cur;}else{parent->_left = cur;}return true;}Node* 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 cur;}}return nullptr;}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->_left){parent->_left = cur->_right;}else{parent->_right = cur->_right;}}}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;}}}else{//左右都不为空// 右树的最小节点(最左节点)Node* parent = cur;Node* subLeft = cur->_right;while (subLeft->_left){parent = subLeft;subLeft = subLeft->_left;}swap(cur->_key, subLeft->_key);if (subLeft == parent->_left)parent->_left = subLeft->_right;elseparent->_right = subLeft->_right;}return true;}}return false;}void InOrder(){_InOrder(_root);std::cout << std::endl;}private:void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);std::cout << root->_key << ":" << root->_value << std::endl;_InOrder(root->_right);}private:Node* _root = nullptr;};
}

       以上就是二叉搜索树的初阶内容了,我们后续在学习map和set时还会使用二叉搜索树来实现,具体我们后面再来分析,下一篇,我们将讲解一些二叉树的相关例题,敬请期待~

       要做一个情绪稳定的成年人,。烦躁的时候千万不要讲话,也不要做任何决定,就安静的待一会。当你内心足够坚定的时候,谁都没有办法影响你的情绪。经历的越多,反而明白的更多,把期待降低,把依赖变少,努力把生活变成自己想要的样子。

相关文章:

C++--二叉搜索树初阶

前言&#xff1a;二叉搜索树是一种常用的数据结构&#xff0c;支持快速的查找、插入、删除操作&#xff0c;C中map和set的特性也是以二叉搜索树作为铺垫来实现的&#xff0c;而二叉搜索树也是一种树形结构&#xff0c;所以&#xff0c;在学习map和set之前&#xff0c;我们先来学…...

Type List(C++ 模板元编程)

定义 类型列表&#xff0c;字面意思就是一个存储类型的列表&#xff0c;例如std::tuple<int, float, double, std::string>就是一个类型列表。 template<typename ...Ts> struct type_list {};基础操作 操作约束&#xff1a;对于所有操作&#xff0c;均要求参数…...

使用老北鼻CharGPT对话查询 Qt/C++ 使用gumbo-parse解析加载的html全过程

记下使用老北鼻CharGPT对话查询 Qt/C解析html网页全过程。 [gumbo-parse] Gumbo是HTML5解析算法作为纯C99库实现&#xff0c;没有外部依赖性。它被设计为其他工具和库的构建模块&#xff0c;比如linters、验证器、模板语言、重构和分析工具。详细说明参考original-README.md 目…...

​ iOS App Store上传项目报错 缺少隐私政策网址(URL)解决方法

一、问题如下图所示&#xff1a; ​ 二、解决办法&#xff1a;使用Google浏览器&#xff08;翻译成中文&#xff09;直接打开该网址 https://www.freeprivacypolicy.com/free-privacy-policy-generator.php 按照要求填写APP信息&#xff0c;最后将生成的网址复制粘贴到隐私…...

设计模式第一课-单例模式(懒汉模式和饿汉模式)

单例模式 个人理解&#xff1a;单例模式实际就是通过类加载的方式获取到一个对象&#xff0c;并且保证这个对象在使用中只有一个&#xff0c;不允许再次被创建 一、懒汉模式 1、懒汉模式的基础写法 代码解释&#xff1a; &#xff08;1&#xff09;、编写LazySingleton类的…...

Yaml文件详解

目录 1、Yaml文件详解 2、详解k8s中的port 3、Service yaml 4、Deployment yaml文件详解 5、Pod yaml文件详解 1、Yaml文件详解 Kubernetes 支持 YAML 和 JSON 格式管理资源对象 JSON 格式&#xff1a;主要用于 api 接口之间消息的传递 YAML 格式&#xff1a;用于配置和管…...

【题解 线段树】[蓝桥杯 2022 省 A] 选数异或

题目描述&#xff1a; [蓝桥杯 2022 省 A] 选数异或 题目描述 给定一个长度为 n n n 的数列 A 1 , A 2 , ⋯ , A n A_{1}, A_{2}, \cdots, A_{n} A1​,A2​,⋯,An​ 和一个非负整数 x x x, 给定 m m m 次查询, 每次询问能否从某个区间 [ l , r ] [l, r] [l,r] 中选择两…...

宠物喂食器方案智能开发设计

现在年轻人特别是在一、二、三线城市的&#xff0c;工作节奏快、加班、出差、旅游成常态&#xff0c;无法经常在宠物身边照看&#xff0c;宠物智能自动喂食机能够解放宠主的双手和解决不能长时间在处的无奈&#xff0c;很好地满足了年轻宠物主照顾宠物的需求。宠物主和宠物都需…...

chatgpt综述阅读理解

Summary of ChatGPT-Related research and perspective towards the future of large language models 摘要 本文总结了语言模型在遵循指令和人类反馈方面的相关工作&#xff0c;包括训练语言模型来理解指令并按照指令执行任务&#xff0c;以及提高语言模型的性能和理解能力的…...

XCTF-RSA-2:baigeiRSA2、 cr4-poor-rsa

baigeiRSA2 题目描述 import libnum from Crypto.Util import number from functools import reduce from secret import flagn 5 size 64 while True:ps [number.getPrime(size) for _ in range(n)]if len(set(ps)) n:breake 65537 n reduce(lambda x, y: x*y, ps) m …...

js 根据word文档模板导出内容

一、创建word导出模板 1、本地创建一个test.docx 2、将最终需要的文档内容及样式编辑完成(图1) 3、将所需动态值的位置,替换为变量参数(图2) 注: 动态值书写 图1 图2 模板值的书写要求 二、项目中使用 1、安装依赖 npm install docxtemplater-image-module-free --save n…...

AIGC | 如何用“Flow”,轻松解决复杂业务问题

随着LLM&#xff08;大语言模型&#xff09;的爆火&#xff0c;不少企业都在寻找通过LLM解决企业业务问题的方法&#xff0c;以达到降本增效的效果。但是&#xff0c;当面对较为复杂的业务问题&#xff08;如&#xff1a;背景资料多、问题分类多、条件判断复杂、涉及模块多等&a…...

多级菜单 树结构 排序 前端 后端 java

目录 省流&#xff1a; 正文&#xff1a; v1.0版 前端传的值&#xff1a; 后端代码&#xff1a; v2.0版 v3.0版 省流&#xff1a; 前端提交过来整个树即可。 给整个树进行sort。代码如下&#xff1a; public static void sort(List<Node> tree){int i 0;for…...

LAN-Free在数据备份时的应用与优势

在灾备领域中&#xff0c;常见的备份架构有LAN、LAN-Free和Server-Free备份&#xff0c;其中LAN备份架构图见图1&#xff0c;LAN-Free备份架构图见图2&#xff0c;Server-Free备份架构图见图3&#xff0c;途中红色箭头为备份数据流量走向&#xff1a; 图 1 图 2 图 3 从图1、图…...

HTML 文档声明和语言设置

HTML 文档声明 DOCTYPE 文档类型声明&#xff0c;用于告诉浏览器的解析器&#xff0c;该以那种 HTML 版本来解析这个文件。 HTML 5 版本声明 <!DOCTYPE html>XHTML 1.0 严格版声明 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http:/…...

【C++基础知识学习笔记】精华版(复习专用)

常用语法 函数重载(Overload) 规则: 函数名相同 参数个数不同、参数类型不同、参数顺序不同 注意: 返回值类型与函数重载无关 调用函数时,实参的隐式类型转换可能会产生二义性 默认参数 C++ 允许函数设置默认参数,在调用时可以根据情况省略实参。规则如下: 默认参数只能…...

探索ChatGPT在学术写作中的应用与心得

随着人工智能的迅猛发展&#xff0c;ChatGPT作为一种强大的自然语言处理模型&#xff0c;逐渐在学术界引起了广泛的关注。本文将探讨ChatGPT在学术写作中的应用&#xff0c;并分享使用ChatGPT进行学术写作时的一些经验和心得。 01 — ChatGPT在学术写作中的应用 1.文献综述和…...

Android:怎么学习才能更好的进大厂呢?

怎么学习才能更好的进大厂呢&#xff1f; 很多朋友都在问这个问题。 其实没有什么特别的技巧&#xff0c;就是依靠自己的毅力和决心。一天做不到&#xff0c;就一个月&#xff1b;一个月做不到&#xff0c;就一年。只要有决心&#xff0c;无论学历或资历如何&#xff0c;都不是…...

CSS标点符号换行问题

最近遇到一个奇怪的现象,元素中中文文本正常显示,但是加了一堆符号后中文文本居然换行了. div{width: 200px;border: 1px solid blue;word-break: break-all;} <div>文本</div>经过研究发现&#xff0c;因为标点符号不允许出现在行首和行尾&#xff0c;连带着符号…...

jdbc Preparestatement防止SQL注入的原理

2023-10-28T03:37:11.264132Z 2 Execute select * from users where username liulemon and password \ or \1\ 1\ 可以看到这一行&#xff0c;预编译时&#xff1f;变成了转义字符 useServerPrepStmtstrue加上这句才能预编译...

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…...

工业安全零事故的智能守护者:一体化AI智能安防平台

前言&#xff1a; 通过AI视觉技术&#xff0c;为船厂提供全面的安全监控解决方案&#xff0c;涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面&#xff0c;能够实现对应负责人反馈机制&#xff0c;并最终实现数据的统计报表。提升船厂…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂

蛋白质结合剂&#xff08;如抗体、抑制肽&#xff09;在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上&#xff0c;高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术&#xff0c;但这类方法普遍面临资源消耗巨大、研发周期冗长…...

深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法

深入浅出&#xff1a;JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中&#xff0c;随机数的生成看似简单&#xff0c;却隐藏着许多玄机。无论是生成密码、加密密钥&#xff0c;还是创建安全令牌&#xff0c;随机数的质量直接关系到系统的安全性。Jav…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)

引言&#xff1a;为什么 Eureka 依然是存量系统的核心&#xff1f; 尽管 Nacos 等新注册中心崛起&#xff0c;但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制&#xff0c;是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...

解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错

出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上&#xff0c;所以报错&#xff0c;到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本&#xff0c;cu、torch、cp 的版本一定要对…...

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...

Java多线程实现之Thread类深度解析

Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

SQL慢可能是触发了ring buffer

简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...