一、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树的定义、查找、插入和删除的更多相关文章

  1. Java创建二叉搜索树,实现搜索,插入,删除操作

    Java实现的二叉搜索树,并实现对该树的搜索,插入,删除操作(合并删除,复制删除) 首先我们要有一个编码的思路,大致如下: 1.查找:根据二叉搜索树的数据特点,我们可以根据节点的值得比较来实现查找,查 ...

  2. AVL树C++实现(插入,删除,查找,清空,遍历操作)

    AVL.h文件代码 #pragma once #include<iostream> #include<stack> #include <assert.h> usin ...

  3. HDU_2871 线段树+vecor的中间插入和删除使用

    本来这个题目就是个合并区间的题,就跟Hotel一样,要插入一段,则找左孩子 合并后的中间区间 右孩子,但是比较恶心的是,他需要实时得到某一段的起终点,或者某个点在第几个段里面,我想过在线段树里面加入几 ...

  4. 二叉搜索树(BST)的插入和删除递归实现

    思路 二叉搜索树的插入 TreeNode InsertRec(rootNode, key) = if rootNode == NULL, return new Node(key) if key > ...

  5. 数据结构与算法16—平衡二叉(AVL)树

    我们知道,对于一般的二叉搜索树(Binary Search Tree),其期望高度(即为一棵平衡树时)为log2n,其各操作的时间复杂度O(log2n)同时也由此而决定.但是,在某些极端的情况下(如在 ...

  6. 牢记!SQL Server数据库开发的二十一条注意点

    如果你正在负责一个基于SQL Server的项目,或者你刚刚接触SQL  Server,你都有可能要面临一些数据库性能的问题,这篇文章会为你提供一些有用的指导(其中大多数也可以用于其它的DBMS). ...

  7. SQL Server数据库开发的二十一条军规

    如果你正在负责一个基于SQL Server的项目,或者你刚刚接触SQL Server,你都有可能要面临一些数据库性能的问题,这篇文章会为你提供一些有用的指导(其中大多数也可以用于其它的DBMS).在这 ...

  8. AVL 树的插入、删除、旋转归纳

    参考链接: http://blog.csdn.net/gabriel1026/article/details/6311339   1126号注:先前有一个概念搞混了: 节点的深度 Depth 是指从根 ...

  9. 与TableView插入、删除、移动、多选,刷新控件

    一.插入.删除.移动.多选 方法一: Cell的插入.删除.移动都有一个通用的方法,就是更新tableView的数据源,再reloadData,这样做实现上是简单一点,但是reloadData是刷新整 ...

随机推荐

  1. 前端性能优化——首屏时间&&白屏时间

    1.首屏时间概念 首屏时间是指用户打开一个网站时,直到浏览器首页面内容渲染完成的时间. 2.白屏时间概念 白屏时间即是,浏览器开始显示内容的时间,所以我们一般认为解析完<head>的时刻, ...

  2. Unity——射线检测(鼠标点击开关门效果)

    Unity射线检测--实现简单的开关门效果 简要:通过鼠标点击来发射一条射线,来获得射线所碰到的物体名称,再通过改变门的Rotation值来实现开关门的效果. 一.代码实现 1.1 简易的场景搭建 注 ...

  3. 【原创】在RT1050 LittleVgl GUI中嵌入中文输入法框架

    时隔一年多终于又冒泡了,哎,随着工作越来越忙,自己踏实坐下来写点东西真是越来越费劲,这篇文章也是准备了好久好久才打算发表出来(不瞒大家,东西做完好久了,文章憋了一年了,当真"高产" ...

  4. C#中winform DataGridView常用修改点

    1.修改列名 一般情况下,从数据库里面读取的列名是英文或者拼音,但是,有时候显示需要中文,这样就需要修改列名了. dgv.Columns[0].HeaderCell.Value="编号&qu ...

  5. 秀++视频算法仓库-厂家对接规约V5

    一.概要 (1)每个算法厂家在秀++云平台上会有一个厂商标识,譬如CS101:算法厂家可能有多个算法引擎,每个引擎有一个标识譬如Q101,引擎可以理解为一个可执行程序,可以同时分析多路算法:每个算法在 ...

  6. Redis系列10:HyperLogLog实现海量数据基数统计

    Redis系列1:深刻理解高性能Redis的本质 Redis系列2:数据持久化提高可用性 Redis系列3:高可用之主从架构 Redis系列4:高可用之Sentinel(哨兵模式) Redis系列5: ...

  7. HMM算法python实现

    基础介绍,后5项为基础5元素 Q = ['q0', 'q1', 'q2', 'q3'] # 状态集合 States,共 N 种状态 V = ['v0', 'v1'] # 观测集合 Observatio ...

  8. [leetcode] 994. Rotting Oranges

    题目 You are given an m x n grid where each cell can have one of three values: 0 representing an empty ...

  9. MyBatis详解(一)

    MyBatis简单介绍 [1]MyBatis是一个持久层的ORM框架[Object Relational Mapping,对象关系映射],使用简单,学习成本较低.可以执行自己手写的SQL语句,比较灵活 ...

  10. Spring02:注解IOC、DBUtils单表CRUD、与Junit整合

    今日内容:基于注解的IOC及IOC的案例 Spring中IOC的常用注解 案例-使用xml方式和注解方式实现单表的CRUD操作 持久层技术选型:DBUtils 改造基于注解的IOC案例,使用纯注解的方 ...