B-Tree的初学习

 写在前面:

前几周,对B树这种数据结构进行了一个初步的学习。当时学习的过程中,我是不断查找网上的资料以及阅读了《数据结构与算法分析-java语言描述》。由于在网上查找资料的过程中,很多资料都是互相抄,然后不加分析就弄出来,我刚刚学习的时候就感到特别迷茫,所以也有了自己想总结知识,写个博客的想法。

我是从网上和书籍上合并资料的,个人见解尚肤浅,如果有错误和疑惑的地方,请大家指出来啦,谢谢~

//

1.为什么会有B-Tree这种数据结构。

由于物理介质的因素,决定了每个存储设备的I/O速度。数据从内存到CPU,和从硬盘到内存是差了好几个数量级别的,前者比后者快了太多了。就好像在同一条宽敞的道路上,前者是轿车,后者是自行车。

上面的是一种比喻点击这儿,大家最好去看一看呀!!这个博客里面有关于磁盘的描述和B-Tree的描述(但是有个地方,求 B树最高高度的时候我感觉是错误的,待会会说到)。超链接里面会给出一些磁盘的知识,挺好的,我就懒得复制啦。

这里用《数据结构与算法分析-java语言描述》中的例子来说明下:

一台500-MIPS的机器可能每秒执行5亿条指令,这是相当快的,主要是因为速度主要依赖电的特性。另一方面,磁盘的操作是机械运动,它的速度主要依赖于转动磁盘和移动磁头的时间,许多以7200RPM选择,即一分钟转7200次。因此,1转占用1/120秒,约等于8.3毫秒(这是非常宽松的估计,9~11毫秒的访问时间更为普遍)。由此可得,磁盘在一秒钟内大概可以转120次。如果不和处理器比较,那么这个磁盘I/O速度听起来还是很好的。可是考虑到处理器的速度,在一秒钟内,5亿条指令的花费相当于120此磁盘访问的时间,也就是着一秒种内,CPU只进行磁盘I/O。换算一下,相当于一次I/O用了400万条指令(书上给的是40万,我感觉中文版是翻译错了,对了,我看的是第三版)。这是令人发指的访问代价。

//

2.B-Tree初步认识

2.1

直接给出B树的特性(如果想知道为什么,需要大家对树有一些了解,还有一些数学知识):

一棵m阶B树的特性(m >= 2):

a.每个结点的子结点最多有m个

b.除了根结点之外,每个结点的子结点至少有ceil(m/2)个,这里是指m/2向上取整,比如ceil(5/2)=3

c.每个结点中的关键字数量n,有这么一个公式:ceil(m/2) - 1 <= n <= m - 1, n是正整数

d.全部叶子结点位于同一层

e.关键字都是依次递增,就是说,这些关键字是有顺序滴

 


上图是一棵B树,大家很容易看出特性c。看结点[D H],它的子结点有三个;看结点[Q T X],它的子结点有4个。其实道理很简单,一个关键字确定了一个范围嘛,比如关键字是10,是不是有比10少和比10大的两个范围~所以我们可以用特性a,b来确定c呀。

这里说下这个结点中的内容。一个结点中,包含1.关键字,2.这个关键字代表真实数据的地址(磁盘上的具体位置),3.子结点的指针,请注意,这里我说的是B树,不是B+树,大家不要搞混淆了哦。

 --

2.2 查找数据

下图是一张随处可见的树


上图中,浅蓝色块代表磁盘块,深蓝色代表关键字,黄色代表子结点指针,红色代表关键字代表的真实数据地址。

查找关键字28所代表的真实数据个过程:

   1.第一次磁盘I/O,把根结点读取到内存,发现目的关键字28在17~35之间,于是确定了范围,通过P2指针(指向了磁盘块3)找到了下一个子结点的地址;

   2.第二次磁盘I/O,把磁盘块3这个结点读到内存,发现目的关键字28在26~30之间,确定了范围,通过P2指针(指向了磁盘块8)找到了下一个子结点的地址;

   3.第三次磁盘I/O,把磁盘块8读到内存里面,匹配到了目的关键字28,并且知道了真实数据地址。

现在,我通过了3次磁盘读取,就找到了我想要的数据。大家想想呀,如果这11块磁盘块不是按照B树这么一种数据结构存储的(就是说这11个磁盘块无序存储的),那么我每次查找,是不是都要把11个磁盘块都读出来,然后才能进行比较,这样读11次就很麻烦啦,

况且真实情况中,很有可能是几百万个磁盘块呀~;

注意事项:

 在这里,这棵是B树呀,虽然在其他博客资料里面,这棵是B+树。而且,上面我找的是关键字28,所以到了叶子结点,假如我找的是关键字65,那就会到第二层结点(根结点是第一层,注意像《算法导论》是把根结点当作第0层)的磁盘块4中,找到深蓝色的65关键字区域啦。

由上面的简单查询过程,我们可以知道,读取磁盘的最多次数就是树的高度,我这棵树是3层,所以最多的读取磁盘次数为3。因此,可以想到,如果一层中可以存取越多的结点,那么树的高度机会越低,从而磁盘I/O的最多次数就越少。在这里还要讲一点,如果存取数据的时候,都存储在同一个磁道,那么转一圈磁盘就能读取同一层的数据(即同层结点),那么就减少了查找数据的时间。由此可得,相邻的数据应该尽量在磁盘中存储在相邻的地方。(具体请大家参考我上面给出的超链接)。

so, 我们来考虑一下最差的情况,即什么情况下B树的高度最高。

首先举个例子,假设现在有100个学生,每个教室可以坐下5个学生,那么就需要20个这样的教室坐下全部的学生啦。如果每个教室可以坐下10个人的话,那就需要10个教室啦~好嘞,这个简单的除法大家都懂。那么我们换成B树,上面提到,每层存储的结点越多,那么树的高度越低,但是,毕竟每层能够存储的结点肯定是有上限的(上面B树特性中提到,每个结点的子结点最多有m个),如果结点越多,那么相对来说,树的高度就会提高。因此可知,怎么样才能让结点最多呢?是的,就像教室一样,如果一个教室只能坐一个教室,那么要坐下100个学生就要100个这样的教室。将教室映射成结点,如果一个结点中存储的关键字越少,那么是不是存储一定量数据的时候,需要的结点就越多。最后将B树最高的高度问题,转变成求最多结点,同时每层存储最少的结点(即,子结点最少)。

假设一棵m阶B树,一共有N个关键字,叶子结点都在h层。(请大家回顾一下上面的B树特性)

层数 结点数 描述
1 1 根结点
2 2 根结点至少有两个孩子
3 2ceil(m/2)  两倍的最少结点
 4  2ceil(m/2)2  红色2代表ceil(m/2)的平方
 h  2ceil(m/2)h-2 红色h-2代表ceil(m/2)的(h-2)次方

 其实很容易知道,现在结点那么多,而每层又存最少的结点,那么层数就自然上去啦。有上面的B树特性知道,第一层和第二层的结点数,从第三层开始,就可以用ceil(m/2)这个函数来决定这层的最少结点数。

通过累加每层的结点数有:1 + 2 + 2ceil(m/2) + 2ceil(m/2)2 + 2ceil(m/2)3 + ..... + 2ceil(m/2)(h-2) 这里的红色小字代表ceil(m/2)的指数。

很明显这是个等比数列的累加啦!由于数学符号很难画,所以我就直接写化简结果。


由上图所示, 第一次=>的结果是获得了全部结点数。

重点是第二次的=>,为什么我要乘以(t-1),大家记不记得,这个算法,是建立在每个结点存最少关键字,每层存最少结点的基础上的。既然我现在知道了全部结点数,那么知道每个结点中的最少关键字为(t-1),如果不明白为什么,就请复习上面的B树特性,这样相乘我就可以知道总的关键字数量啦,结点数 乘以 结点容量嘛。

第三次=> 化简公式,由于前提已经假设了一共有n个关键字,这里的用的是>=比较符号,其实可以换成等于号啦,因此就可以知道h的最大值啦~(原谅我字不好。。。。)。最后,把t再换回ceil(m/2)就好。

在前面我提高,我上面给出的超链接中的那个博客,他也说到了B树最高高度的情况,但是里面的假设了某个非叶子结点的关键字是N个,但是最后结论的时候却把这个N套进了B树的总关键字数量,所以有疑问,于是我在这儿指出来,希望大家也思考下~

 2.2.B树的增加和删除关键字

其实其实其实其实其实。。我真的好懒阿。。。我很想说,上面给出的那个链接里面的对B树关键字增删就说的挺好的,所以!!!!!!

http://blog.csdn.net/v_july_v/article/details/6530142

客官们拿好不谢,因为我也是从这儿学习增删的内容,并且觉得挺好的。

注意事项:

1.是对关键字的增删,从而导致了对结点的增删;

2.为什么说“关键字多了就分裂,少了就合并”,其实这就是对树的旋转,让树平衡。具体就是,确保这棵树的同一层的每个分支都能够平衡,不会说左边的分支特别长,而右边的分支特别短,出现这种畸形树。所以增删的时候需要通过“关键字多了就分裂,少了就合并”这种旋转方式来保证这棵树是平衡的。

好了,对B树的简单了解就到这儿,欢迎客官们指出错误~

猜你喜欢

转载自cloudgan.iteye.com/blog/2383956