二十一、B树的定义、查找、插入和删除
一、B树的定义
一棵m阶的B树,或为空树,或为满足下列特性的m叉树:
(1)树中每个结点至多有m棵子树;
(2)B树是所有结点的平衡因子均等于0的多路平衡查找树;
(3)若根结点不是叶子结点,则至少有两棵子树;
(4)除根之外的所有非终端结点至少有⌈m/2⌉棵子树;
(5)所有的叶子结点都出现在同一层次上,并且不带信息,通常称为失败结点(失败结点并不存在,指向这些结点的指针为空。引入失败结点是为了便于分析B树的查找性能);
(6)所有的非终端结点最多有m-1个关键字,结点的结构如图所示。

其中:
- ${K_i}(i = 1,...,n)$ 为关键字,且 ${K_i} < {K_{i + 1}}(i = 1,...,n - 1)$。
- ${P_i}(i = 0,...,n)$ 为指向子树根结点的指针,且指针 ${P_{i - 1}}$ 所指子树中所有结点的关键字均小于 ${K_i}(i = 1,...,n)$ , ${P_{n}}$ 所指子树中所有结点的关键字均大于 ${K_{n}}$。
- $n(\left\lceil {m/2} \right\rceil - 1 \le n \le m - 1)$ 为关键字的个数(或 $n+1$ 为子树个数)。
从上述定义可以看出,对任一关键字 ${K_i}$ 而言, ${P_{i - 1}}$ 相当于指向其“左子树”, ${P_{i}}$ 相当于指向其“右子树”。
B树具有平衡、有序、多路的特点。
二、B树的查找
B树的查找从根结点开始,重复以下过程:若给定关键字等于结点中某个关键字Ki,则查找成功;若给定关键字比结点中的K1小,则进入指针A0。指向的下一层结点继续查找;若在两个关键字Ki-1和Ki之间,则进入它们之间的指针Ai-1从一指向的下一层结点继续查找;若比该结点所有关键字大,则在其最后一个指针An指向的下一层结点继续查找;若查找到叶子结点,则说明给定值对应的数据记录不存在,查找失败。
查看代码
#define m 3 //B树的阶
typedef struct BTNode
{
int keynum; //结点当前关键字个数
KeyType key[m+1]; //关键字数组,key[0]未用
struct BTNode *parent; //双亲结点指针
struct BTNode *ptr[m+1]; //孩子结点指针数组
Record *recptr[m+1]; //记录指针向量,0号单元未用
}BTNode, *BTree;
typedef struct
{
BTree pt; //指向找到的结点
int i; //1<=i<=m,在结点中的关键字位序
int tag; //1:查找成功;0:查找失败
}Result;
int Search(BTree p,int key)
{ //在p->key[1...p->keynum]找k
int i=1;
while(i<=p->keynum && k>p->key[i]) i++;
return i;
}
Result SearchBTree(BTree T,KeyType key)
{ //在m阶B树T上查找关键字key,返回结果(pt,i,tag)
//若查找成功,则特征值tag=1,指针pt所指结点中第i个关键字等于key
//否则特征值tag=0,等于key的关键字应插在指针pt所指结点中第i和第i+1个关键字之间
int i=0;
int found=FALSE;
BTree p=T;
BTree q=NULL; //初始化,p指向待查结点,q指向p的双亲
while(p && !found)
{
i=Search(p,key); //在p->key[1...keynum]中查找i,使得:p->key[i]<=key<p->key[i+1]
if(i>0 && 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树的插入过程可描述为,利用B树的查找操作查找关键字k的插入位置。若找到,则说明该关键字已经存在,直接返回;否则查找操作必失败于某个最底层的非终端结点上,在该结点插入后,若其关键字总数n未达到m,算法结束,否则需分裂结点。
分裂的方法是,生成一新结点,从中间位置把结点(不包括中间位置的关键字)分成两部分。前半部分留在旧结点中,后半部分复制到新结点中,中间位置的关键字连同新结点的存储位置插入到父结点中。如果插入后父结点的关键字个数也超过m-1,则继续分裂。这个向上分裂过程如果持续到根结点,则会产生新的根结点。
结点分裂示例

B树的插入动图示例

三阶B树插入示例

void split(BTree& q, int s, BTree& ap)
{ //将q结点分裂成两个结点,前一半保留在原节点,后一半移入ap所指新结点
int i = 0;
int j = 0;
int n = q->keynum;
ap = (BTNode*)malloc(sizeof(BTNode)); //生成新结点
ap->ptr[0] = q->ptr[s];
for (i = s + 1, j = 1; j <= n; i++, j++)
{ //后一半移入ap结点
ap->key[j] = q->key[i];
ap->ptr[j] = q->ptr[i];
}
ap->keynum = n - s;
ap->parent = q->parent;
for (i = 0; i <= n - s; i++) //修改新结点的子节点的parent域
{
if (ap->ptr[i] != NULL)
{
ap->ptr[i]->parent = ap;
}
q->keynum = s - 1; //q结点的前一半保留,修改keynum
}
}
void newRoot(BTree& t, BTree p, int x, BTree ap)
{ //生成新的根节点
t = (BTNode*)malloc(sizeof(BTNode));
t->keynum = 1;
t->ptr[0] = p;
t->ptr[1] = ap;
t->key[1] = x;
if (p != NULL)
p->parent = t;
if (ap != NULL)
ap->parent = t;
t->parent = NULL; //新根的双亲是空指针
}
void Insert(BTree& q, int i, int x, BTree ap)
{ //关键字x和新结点指针ap分别插入到q->key[i]和q->ptr[i]
int j = 0;
int n = q->keynum;
for (j = n; j >= i; j--)
{
q->key[j + 1] = q->key[j];
q->ptr[j + 1] = q->ptr[j];
}
q->key[i] = x;
q->ptr[i] = ap;
if (ap != NULL)
ap->parent = q;
q->keynum++;
}
void InsertBTree(BTree& t, int k, BTree q, int i)
{ //在B树t中q结点的key[i-1]和key[i]之间插入关键字k
//若插入后结点关键字个数等于B树的阶,则沿双亲指针链进行结点分裂,使t仍是m阶B树
int x = 0;
int s = 0;
int finished = 0;
int needNewRoot = 0;
BTree ap;
if (!q)
newRoot(t, NULL, k, NULL); //生成新的根节点
else
{
x = k;
ap = NULL;
while (needNewRoot == 0 && finished == 0)
{
Insert(q, i, x, ap); //x和ap分别插入到q->key[i]和q->ptr[i]
if (q->keynum < m)
finished = 1; //插入完成
else
{ //分裂q结点
s = (m + 1) / 2;
split(q, s, ap);
x = q->key[s];
if (q->parent != NULL)
{
q = q->parent;
i = Search(q, x); //在双亲结点中查找x的插入位置
}
else
needNewRoot = 1;
}
}
if (needNewRoot == 1) //t是空树或者根结点已分裂为q和ap结点
newRoot(t, q, x, ap); //生成含信息(q,x,ap)的新的根节点t
}
}
四、B树的删除

二十一、B树的定义、查找、插入和删除的更多相关文章
- Java创建二叉搜索树,实现搜索,插入,删除操作
Java实现的二叉搜索树,并实现对该树的搜索,插入,删除操作(合并删除,复制删除) 首先我们要有一个编码的思路,大致如下: 1.查找:根据二叉搜索树的数据特点,我们可以根据节点的值得比较来实现查找,查 ...
- AVL树C++实现(插入,删除,查找,清空,遍历操作)
AVL.h文件代码 #pragma once #include<iostream> #include<stack> #include <assert.h> usin ...
- HDU_2871 线段树+vecor的中间插入和删除使用
本来这个题目就是个合并区间的题,就跟Hotel一样,要插入一段,则找左孩子 合并后的中间区间 右孩子,但是比较恶心的是,他需要实时得到某一段的起终点,或者某个点在第几个段里面,我想过在线段树里面加入几 ...
- 二叉搜索树(BST)的插入和删除递归实现
思路 二叉搜索树的插入 TreeNode InsertRec(rootNode, key) = if rootNode == NULL, return new Node(key) if key > ...
- 数据结构与算法16—平衡二叉(AVL)树
我们知道,对于一般的二叉搜索树(Binary Search Tree),其期望高度(即为一棵平衡树时)为log2n,其各操作的时间复杂度O(log2n)同时也由此而决定.但是,在某些极端的情况下(如在 ...
- 牢记!SQL Server数据库开发的二十一条注意点
如果你正在负责一个基于SQL Server的项目,或者你刚刚接触SQL Server,你都有可能要面临一些数据库性能的问题,这篇文章会为你提供一些有用的指导(其中大多数也可以用于其它的DBMS). ...
- SQL Server数据库开发的二十一条军规
如果你正在负责一个基于SQL Server的项目,或者你刚刚接触SQL Server,你都有可能要面临一些数据库性能的问题,这篇文章会为你提供一些有用的指导(其中大多数也可以用于其它的DBMS).在这 ...
- AVL 树的插入、删除、旋转归纳
参考链接: http://blog.csdn.net/gabriel1026/article/details/6311339 1126号注:先前有一个概念搞混了: 节点的深度 Depth 是指从根 ...
- 与TableView插入、删除、移动、多选,刷新控件
一.插入.删除.移动.多选 方法一: Cell的插入.删除.移动都有一个通用的方法,就是更新tableView的数据源,再reloadData,这样做实现上是简单一点,但是reloadData是刷新整 ...
随机推荐
- 齐博x1标签实例:调用多个圈子同时调用相关会员
看这一篇之前,请先看上一篇,因为他们有关联性比如要实现这样的效果 可以通过下面的代码可以实现 {qb:tag name="xxx" type="qun" row ...
- etcd实现分布式锁
转载自:etcd实现分布式锁 当并发的访问共享资源的时候,如果没有加锁的话,无法保证共享资源安全性和正确性.这个时候就需要用到锁 1.需要具备的特性 需要保证互斥访问(分布式环境需要保证不同节点.不同 ...
- docker swarm快速部署redis分布式集群
环境准备 四台虚拟机 192.168.2.38(管理节点) 192.168.2.81(工作节点) 192.168.2.100(工作节点) 192.168.2.102(工作节点) 时间同步 每台机器都执 ...
- LAPM概述及配置
一.LAMP概述 1.1LAMP的概念 LAMP架构是目前成熟的企业网站应用模式之一,指的是协同工作的一整套系统和相关软件,能够提供动态web站点服务及其应用开发环境 LAMP是一个缩写词,具体包括L ...
- JAVA开发搞了一年多的大数据,究竟干了点啥
JAVA开发搞了一年多大数据的总结 2021年7月份加入了当前项目组,以一个原汁原味的Java开发工程师的身份进来的,来了没多久,项目组唯一一名大数据开发工程师要离职了,一时间一大堆的数据需求急需 ...
- Go语言核心36讲40
我相信,经过上一次的学习,你已经对strings.Builder和strings.Reader这两个类型足够熟悉了. 我上次还建议你去自行查阅strings代码包中的其他程序实体.如果你认真去看了,那 ...
- Go语言核心36讲07
在前文中,我解释过代码块的含义.Go语言的代码块是一层套一层的,就像大圆套小圆. 一个代码块可以有若干个子代码块:但对于每个代码块,最多只会有一个直接包含它的代码块(后者可以简称为前者的外层代码块). ...
- 1、Docker最新入门教程-Docker概述
1. Docker概述 Docker 是一个用于开发.运输和运行应用程序的开放平台.Docker 使您能够将应用程序与基础架构分开,以便您可以快速交付软件.使用 Docker,您可以像管理应用程序一样 ...
- 当resource bundle 的多语言文件里包含引号'时
背景 项目中使用Spring的ReloadableResourceBundleMessageSource这个类来实现多语言,有一次字符串里包含引号'时,解析时出了问题,一起来看一下吧 例子 resou ...
- ES文件浏览器局域网传输文件分析
软件下载链接 1.前言 我之前从手机上传输到电脑上一些apk进行分析,都是使用es文件浏览器这款软件获取 app,传输方面使用QQ,这样很麻烦,走外网流量暂且不提,总是感觉浪费掉了局域网这个环境.简单 ...