目录

  • 前言
  • 1. 概念指引
    • 1.1 二叉排序树
    • 1.2 平衡二叉树
  • 2. B树
  • 3. B+树
  • 4. 总结

前言

该笔记取决于 天勤的数据结构笔记

本文主要讲解b树和b+树的概念以及基本的代码逻辑

在讲解这部分知识时候,先科普一下一些基本概念作为入门了解

二叉排序树以及二叉平衡树

1. 概念指引

1.1 二叉排序树

二叉排序树 (Binary Sort Tree) 又称二叉查找树,它是一种对排序和查找都很有用的特殊二叉树

二叉排序树的定义

二叉排序树或者是一棵空树,或者是具有下列性质的二叉树:
(1) 若它的左子树不空,则左子树上所有结点的值均小千它的根结点的值;
(2) 若它的右子树不空,则右子树上所有结点的值均大千它的根结点的值;
(3) 它的左、 右子树也分别为二叉排序树。

二叉排序树是递归定义的。
由定义可以得出二叉排序树的一个重要性质:中序遍历一棵二叉树时可以得到一个结点值递增的有序序列

二叉排序树的二叉链表存储表示 具体代码结构如下:

typedef struct
KeyType key;
InfoType otherinfo;
}ElemType;
typedef struct BSTNode
{
ElemType data;
struct BSTNode *lchild,*rchild;
}BSTNode,*BSTree;

二叉排序树的查找
具体的算法步骤为:

  1. 若二叉排序树为空, 则查找失败,返回空指针。
  2. 若二叉排序树非空, 将给定值key与根结点的关键字T->data.key进行比较:
    • 若key等于T->data.key, 则查找成功,返回根结点地址;
    • 若key小于T->data.key, 则递归查找左子树;
    • 若key大于T->data.key, 则递归查找右子树。
BSTree SearchBST (BSTree- T, KeyType key)
{//在根指针T所指二叉排序树中递归地查找某关键字等于key的数据元素
//若查找成功 , 则返回指向该数据元素结点的指针, 否则返回空指针
if ((! T) || key==T->data. key) return T; //查找结束
else if (key<T->data. key) return SearchBST (T->lchild, key); //在左子树中继续查找
else return SearchBST (T->rchild, key) ; //在右子树中继续查找
}

二叉排序树上的查找和折半查找相差不大。但就维护表的有序性而言,二叉排序树更加有效,因为无需移动记录,只需修改指针即可完成对结点的插入和删除操作。因此,对于需要经常进行插入、 删除和查找运算的表,采用二叉排序树比较好。

二叉排序树的插入

二叉排序树的插入操作是以查找为基础的。要将一个关键字值为key的结点*S 插入到二叉排序树中,则需要从根结点向下查找,当树中不存在关键字等千key的结点时才进行插入。新插入的结点一定是一个新添加的叶子结点,并且是查找不成功时查找路径上访问的最后一个结点的左孩子或右孩子结点。

算法步骤如下:

  1. 若二叉排序树为空,则待插入结点*S 作为根结点插入到空树中。
  2. 若二叉排序树非空,则将key与根结点的关键字T->data.key进行比较:
    • 若key小于T->data.key, 则将S 插入左子树;
    • 若key大千T->data.key, 则将
    S 插入右子树。
void InsertBST(BSTree &T,ElemType e}
{//当二叉排序树 T中不存在关键字等千e.key的数据元素时, 则插入该元素
if (!T}
{//找到插入位置 , 递归结束
S=new BSTNode; //生成新结点*S
S->data=e; //新结点*S的数据域置为e
S->lchild=S->rchild=NULL; //新结点*S作为叶子结点
T=S; //把新结点*S链接到已找到的插入位置
}
else if (e. key<T->data. key) InsertBST(T->lchild, e ); //将*S插入左子树
else if (e.key> T->da七a.key)InsertBST(T->rchild, e); //将*S插入右子树
}

二叉排序树的创建

二叉排序树的创建是从空的二叉排序树开始的, 每输入一个结点, 经过查找操作, 将新结点插入到当前二叉排序树的合适位置。

算法步骤:

  1. 将二叉排序树T初始化为空树。
  2. 读入一个关键字为key的结点。
  3. 如果读入的关键字key不是输入结束标志,则循环执行以下操作:
    • 将此结点插入二叉排序树T中;
    •读入一个关键字为 key 的结点。
void CreatBST(BSTree &T)
{//依次读人一个关键字为key的结点, 将此结点插人二叉排序树T中
T=NULL; //将二叉排序树T初始化为空树
cin>>e;
while(e.key!=ENDFLAG)
{InsertBST(T,e); cin>>e;
}

从上面的插入过程还可以看到, 每次插入的新结点都是二叉排序树上新的叶子结点, 则在进行插入操作时, 不必移动其他结点, 仅需改动某个结点的指针, 由空变为非空即可。 这就相当千在一个有序序列上插入一个记录而不需要移动其他记录

二叉排序树的删除

被删除的结点可能是二叉排序树中的任何结点, 删除结点后, 要根据其位置不同修改其双亲结点及相关结点的指针, 以保持二叉排序树的特性

void DeleteBST(BSTree &T,KeyType key)
{//从二叉排序树 T 中删除关键字等千 key 的结点
p=T;f=NULL; //初始化 /*------------下面的 while 循环从根开始查找关键字等于 key 的结点*p---------------*/while (p) {if(p->data.key==key) break; //找到关键字等于 key 的结点*p, 结束循环f=p; //*f 为*p 的双亲结点if(p->data.key>key) p=p->lchild; //在*p 的左子树中继续查找else p=p->rchild; //在*p 的右子树中继续查找}if (!p) return; //找不到被删结点则返回//----考虑3种情况实现p 所指子树内部的处理: *p 左右子树均不空、 无右子树、 无左子树if ((p->lchild) && (p->rchild)) //被删结点*p 左右子树均不空{q=p; s=p->lchild; while (s->rchild) //在*p 的左子树中继续查找其前驱结点,即最右下结点{q=s; s=s->rchild; //向右到尽头}p->data=s->data; //s 指向被删结点的 “前驱"if(q!=p) q->rchild=s->lchild; //重接*q 的右子树else q->lchild=s->lchild; //重接*q 的左子树delete s; return; }else if (! p->rchild) {q=p; p=p->lchild; //被删结点*p 无右子树, 只需重接其左子树) else if (!p- > lchild) {q=p; p=p->rchild; //被删结点*p 无左子树, 只需重接其右子树}//--将 p 所指的子树挂接到其双亲结点*f 相应的位置--*/if(!f) T=p; //被删结点为根结点else if(q==f->lchild) f->lchild=p; else f->rchild=p; delete q;
}

1.2 平衡二叉树

二叉排序树查找算法的性能取决于二叉树的结构,而 二叉排序树的形状则取决于其数据集。如果数据呈有序排列,则二叉排序树是线性的,查找的时间复杂度为O(n); 反之,如果二叉排序树的结构合理,则查找速度较快,查找的时间复杂度为 O(lo2n)。事实上,树的高度越小,查找速度越快。因此,希望二叉树的高度尽可能小。

平衡二叉树或者是空树,或者是具有如下特征的二叉排序树:
(1 )左子树和右子树的深度之差的绝对值不超过1;
(2)左子树和右子树也是平衡二叉树。

若将二叉树上结点的平衡因子(Balance F a ctor, BF)定义为该结点左子树和右子树的深度之差,则平衡二叉树上所有结点的平衡因子只可能觅-1、0和1。只要二叉树上有一个结点的平衡因子的绝对值大于1 则该二叉树就是不平衡的



平衡二叉树的调整过程
插入结点时, 首先按照二叉排序树处理, 若插入结点后破坏平衡二叉树的特性, 需对平衡二叉树进行调整。

调整方法是:找到离插入结点最近且平衡因子绝对值超过1的祖先结点, 以该结点为根的子树称为最小不平衡子树, 可将重新平衡的范围局限于这棵子树

假设表中关键字序列为(13, 24, 37, 90, 53)。

一般情况下,假设最小不平衡子树的根结点为 A, 则失去平衡后进行调整的规律可归纳为4种情况

(1) LL 型:由于在 A 左子树根结点的左子树上插入结点,A的平衡因子由 1 增至 2, 致使以A为根的子树失去平衡,则需进行一次向右的顺时针旋转操作

例子如下:

(2) RR 型:由于在 A 的右子树根结点的右子树上插入结点, A 的平衡因子由-1 变为-2,致使以 A 为根结点的子树失去平衡,则需进行一次向左的逆时针旋转操作

例子如下:

(3) LR型:由于在A的左子树根结点的右子树上插入结点, A的平衡因子由1增至2,致使以A为根结点的子树失去平衡, 则需进行两次旋转操作。 第一次对B及其右子树进行逆时针旋转, C转上去成为B的根, 这时变成了LL型, 所以第二次进行LL型的顺时针旋转即可恢复平衡。 如果C原来有左子树, 则调整C的左子树为B的右子树


例子如下:

(4) RL 型:由于在 A 的右子树根结点的左子树上插入结点, A 的平衡因子由-1 变为-2,,致使以 A 为根结点的子树失去平衡, 则旋转方法和 LR 型相对称, 也需进行两次旋转, 先顺时针右旋, 再逆时针左旋

例子如下:

2. B树

一棵m阶的B树,或为空树,或为满足下列特性的m叉树:

  • 树中每个结点至多有m棵子树;
    -若根结点不是叶子结点,则至少有两棵子树;
  • 除根之外的所有非终端结点至少有「m/2 棵子树;
  • 所有的叶子结点都出现在同一层次上,并且不带信息,通常称为失败结点(失败结点并不存在,指向这些结点的指针为空。引入失败结点是为了便于分析B-树的查找性能);
  • 所有的非终端结点最多有m- 1个关键字,因为他是m叉数

结点的结构如图


(1)所有叶子结点均在同一层次,这体现出其平衡的特点。
(2 ) 树中每个结点中的关键字都是有序的,且关键字Ki;"左子树” 中的关键字均小于Kj;, 而 其 “右子树” 中的关键字均大于Ki;, 这体现出其有序的特点。(平衡二叉树)
(3 )除叶子结点外,有的结点中有一个关键字,两棵子树,有的结点中有两个关键字,三棵子树,这种4阶的B-树最多有三个关键字,四棵子树,这体现出其多路的特点

具体的定义结构如下:

== B数查找==

Result SearchBTree(BTree T,KeyType key)
{//在 m 阶 B-树 T 上查找关键字 key, 返回结果(pt,i, tag)
//若查找成功,则特征值 tag=l, 指针 pt 所指结点中第 J. 个关键字等千 key
//否则特征值 tag=O, 等千 key 的关键字应插入在指针 pt 所指结点中第 1 和第迁1 个关键字之间p=T;q=NULL;found=FALSE;i=O; //初始化, p 指向待查结点, q 指向 p 的双亲while (p&& ! found) {i=Search(p,key); //在 p-> key [ 1 .. keynum]中查找 i, 使得: p->key[i] <=key<p->key[i+l]if(i>O&&p->key[i]==k) found=TRUE; //找到待查关键字else{q=p; p=p->ptr[i];} if (found) return (p, i, 1); //查找成功else return(q,i,0); //查找不成功,返回K的插人位置信息
}

插入
B树是动态查找树, 因此其生成过程是从空树起,在查找的过程中通过逐个插入关键字而得到

但由于B树中除根之外的所有终端结点中的关键字个数必须大于等千【 m/2 】-1 因此,每次插入一个关键字不是在树中添加一个叶子结点,而是首先在最低层的某个终端结点中添加一个关键字,若该结点的关键字个数不超过m-1 则插入完成,否则表明结点已满,要产生结点的 “分裂",将此结点在 同一层分成两个结点。

一般情况下,结点分裂方法是:以中间关键字为界,把结点一分为二,成为两个结点,并把中间关键字向上插入到双亲结点上,若双亲结点巳满,则采用同样的方法继续分解。最坏的情况下,一直分解到树根结点,这时B树高度增加1。

演示一个3阶的b树的具体过程

插入30 从上往下找

插入 26之后,已经超出了3阶

所以会分裂中间的30上去

插入85之后,已经超出了3阶


往上分裂一个中间的70,又超出了3阶

继续往上分裂

具体的代码过程如下:

3. B+树

更适合用于文件索引系统

一棵m阶的 B+ 树和m阶的 B-树的差异在于:

  1. 有n棵子树的结点中含有n个关键字;
  2. 所有的叶子结点中包含了全部关键字的信息,以及指向含这些关键字记录的指针,且子结点本身依关键字的大小自小而大顺序链接;
  3. 所有的非叶子节点可以看成是索引部分,结点中仅含有其子树(根结点)中的最大(或最小)关键字

通常在B+树上有两个头指针,一个指向根结点另一个指向关键字最小的叶子结点。

因此,可以对 B+树进行两种查找运算: 一种是从最小关字起顺序查找,另一种是从根结点开始,进行随机查找


具体的代码逻辑:

  • 查找:若非终端结点上的关键字等于给定值, 并不终止,而是继续向下直到叶子结点。因此,在B+树中,不管查找成功与否,每次查找都是走了一条从根到叶子结点的路径。B+树不仅能够有效地查找单个关键字,而且更适合查找某个范围内的所有关键字
  • 插入:仅在叶子结点上进行插入,当结点中的关键字个数大于m时要分裂成两个结点,它们所含关键字的个数分别介于 (m+1/2)的下线和 (m+1/2)的上线,它们的双亲结点中应同时包含这两个节点中的最大关键字
  • 删除: B+树的删除也仅在叶子结点进行,当叶子结点中最大关键字被删除时,其 在非端结点中的值可以作为一个 “分界关键字“ 存在。若因删除 而使结点中关键字的个数少于「m/2 时,其和兄弟结点的合 并过程亦和B-树类似

4. 总结

  • b数: 每个节点都存储key和data,叶子节点指针为null。
  • b+树:只有叶子节点存储data,叶子节点包含了这棵树的所有键值,叶子节点不存储指针。每个叶子节点增加一个指向相邻叶子节点的指针非叶子节点上是不存储数据的,仅存储键值

任何数据结构都是应用到实际场景中
为了加深印象
举例一个数据结构应用的例子

为什么数据库是B+树而不是B树呢

  • 因为它内节点不存储data,这样一个节点就可以存储更多的key
  • B+树索引的所有数据均存储在叶子节点,而且数据是按照顺序排列的。那么B+树使得范围查找,排序查找,分组查找以及去重查找变得异常简单。而B树因为数据分散在各个节点,要实现这一点是很不容易的

树高度越小,I/O次数越少。

再者MySQL的两种搜索引擎可看我之前的文章
Mysql的两种存储引擎详细分析及区别(全)

具体为啥innodb的io次数少,这是因为

Innodb中的主键索引和实际数据时绑定在一起的,也就是说Innodb的一个表一定要有主键索引,如果一个表没有手动建立主键索引,Innodb会查看有没有唯一索引,如果有则选用唯一索引作为主键索引,如果连唯一索引也没有,则会默认建立一个隐藏的主键索引(用户不可见)。(用户没有指定的话会自己找生产一个隐藏列Row_id来充当默认主键)

另外,Innodb的主键索引要比MyISAM的主键索引查询效率要高(少一次磁盘IO),并且比辅助索引也要高很多。

innodb通过辅助索引的索引值找到主键索引,之后主键索引有具体的data值以及索引信息,都存储在里

【数据结构】B树和B+树的笔记详细诠释相关推荐

  1. 数据结构(C语言版)严蔚敏(树、二叉树的相关概念笔记)

    数据结构(C语言版)严蔚敏(树的相关概念笔记) 1. 树中一个节点的孩子个数称为该节点的度,树中节点的最大度数称为树的度: 2. 度大于0的节点称为[分支节点](非终端节点),度为0的节点称为[叶子节 ...

  2. 【数据结构笔记】B树和B+树的实现,哈希查找,STL中的hash_map和unordered_map容器用法

    B和B+树 哈希查找 用开放定址法解决哈希冲突的哈希查找算法 链地址法: 利用哈希表查找一个字符串中第一个只出现一次的字符 hash_map和unordered_map 设计算法删除重复的元素 设计算 ...

  3. 算法学习笔记——数据结构:哈夫曼树、带权路径长度WPL、哈夫曼编码

    引入 合并果子问题如下: 有n堆果子,每次可以合并任意两堆果子,耗费体力值为[两堆果子数之和],最终在n-1次合并后,得到一堆果子. 给出合并的方案,使得耗费的体力值最小 例如有3堆果子,质量依次为1 ...

  4. 学习笔记:可持久化线段树(主席树):静态 + 动态

    学习笔记:可持久化线段树(主席树):静态 + 动态 前置知识: 线段树.线段树分享可以看:@秦淮岸.@ZYzzz.@妄想の岚がそこに 树状数组.\(BIT\)分享可以看:@T-Sherlock.Chi ...

  5. 算法小讲堂之B树和B+树(浅谈)|考研笔记

    文章目录 一.前言 二.定义 三.原理 3.1 树的高度 3.2 查找操作 3.3 插入操作 3.4 删除操作 3.4.1 删除关键字是非叶子结点 3.4.2 删除关键字是叶子结点 四.B+树 五.应 ...

  6. 空间数据结构(四叉树、八叉树、BVH树、BSP树、k-d树)

    转载地址:https://www.cnblogs.com/KillerAery/p/10878367.html 1. 前言: 在游戏程序中,利用空间数据结构加速计算往往是非常重要的优化思想,空间数据结 ...

  7. 区块链学习笔记16——ETH交易树和收据树

    区块链学习笔记16--ETH交易树和收据树 学习视频:北京大学肖臻老师<区块链技术与应用> 笔记参考:北京大学肖臻老师<区块链技术与应用>公开课系列笔记--目录导航页 交易树和 ...

  8. 数据结构——第六章(树)

    1. 树 1.1 树的基本概念 树:由N个节点(N>=0)构成的集合,有且仅有一个根节点,且树是递归定义的结构. 当n>1时,有m个互不相交的有限集合(判断是否为树:观察他们的子树是否相交 ...

  9. 【数据结构与算法基础】树与二叉树的互化

    前言 数据结构,一门数据处理的艺术,精巧的结构在一个又一个算法下发挥着他们无与伦比的高效和精密之美,在为信息技术打下坚实地基的同时,也令无数开发者和探索者为之着迷. 也因如此,它作为博主大二上学期最重 ...

最新文章

  1. python字符编码讲解_python 字符编码讲解
  2. react native中一次错误排查 Error:Error: Duplicate resources
  3. way.js - 轻量级、持久化的双向绑定JS库
  4. 使用gparted-LiveCD对centos分区调整---virtualbox磁盘调整序2
  5. Java函数编码_转[收集java的常用函数代码]
  6. ubuntu 18.04安装与配置 Redis
  7. BZOJ 4031: [HEOI2015]小Z的房间 Matrix-Tree定理
  8. python如何保存图像_Python应用引擎:如何保存图像?
  9. matlab实现qr分解
  10. Python 3.5安装JPype
  11. Linux --- 常用命令
  12. 华为云备份会上传私密相册吗_华为、小米都是国产手机,为啥文件夹却是“英文”?哪些能删除?...
  13. visual foxpro v6.0官方版
  14. 台式计算机运行慢怎么样能提高速度,如何提高电脑的运行速度,让电脑快起来!...
  15. 台式计算机睡眠了怎么唤醒,分享大家几种电脑深度睡眠怎么唤醒方法
  16. Linux---Apache网页优化---网页压缩
  17. Orica 如何维护安全、质量、管理风险、高标准的客户服务和员工福利所需的大量文档和内容
  18. 凡客登录页面html代码,简洁的凡客购物商城首页模板源码
  19. python3分钟快迅制造一张精美的地图海报
  20. SBT下载特别慢的问题解决

热门文章

  1. 第三章 产品数据 第3节 物料清单BOM
  2. mysql修改时区为utc
  3. Pycharm专业版下载、安装、与Anaconda配置、中文化及字体设置、Cracking(自行翻译)方法
  4. (转)企业级NFS网络文件共享服务
  5. Qt操作Word文档
  6. kindeditor图片上传 jsp版
  7. 【oracle安装】
  8. 二次规划(1):Lagrange法
  9. C# Winform 文本面板带滚动条
  10. Python程序设计基础案例