标签:
一,B树的定义及介绍
为什么会有B树?
熟悉的树的结构有二叉树查找树或者平衡二叉树……平衡二叉树保证最坏情况下各个操作的时间复杂度为O(logN),但是为了保持平衡,在插入或删除元素时,需要进行旋转啊...一系列操作,因此实现起来比较复杂。而对于二叉查找树,基本操作在最坏情况下会出现O(N)的时间复杂度。总之,这些树都是针对于内存中的数据操作,它们每个结点最多只有两个孩子,当数据量大时(结点数目很多),就会导致树很高。但由于基本操作(查找元素、插入元素)都是在内存中实现,因此,树高点也就没有太大的关系。
试想,如果树中的结点数据 是存储在磁盘上的,每访问一个结点需要进行一次磁盘的读取操作,那么树的高度就很重要了。因为,磁盘访问的代价(速度)远远大于内存访问的代价。对于7200转的硬盘而言,访问一次磁盘大约需要8.3ms,而对于4GHz的CPU而言,8.3ms不知可以执行多少次指令了。
因此,B树一个很重要的特征就是:高度小。
那如何让高度变小呢?让每个结点可以拥有多个(远远大于2)孩子就可以了。但是,为了在插入、删除中仍然保持B树的性质(比如高度要低),还需要对B树做一些其他方面的规定:(实际实现过程中可能不同)。
其中最重要的规定是:每个结点最多包含多少个关键字(项),最少需要包含多少个关键字。
这里,给出一个具体的M阶 B树定义(《数据结构与算法分析》MAW著)
①数据项 只存储在树叶上。(数据项就是实实在在的数据,而不是索引)
②非叶子结点最多可以 存储 M-1个关键字以指示搜索的方向(这里的关键字是指索引)。
这里的M-1个关键字是按从小到大的顺序排序的。M-1个关键字,就有M个指针,指向进一步查找的路径。
③树的根或者是一片树叶,或者其儿子数在 2 到 M之间
④除根外,所有非树叶节点的儿子数在 【M/2】 和 M 之间 【M/2】表示,M/2并向上取整
非叶子结点的儿子数最少为【M/2】,这就是为了保证每个结点足够多的孩子,从而使树的高度不至于太大。
⑤所有的树叶都在相同的深度上并有【L/2】 和 L 个数据项
这里表明,真正的数据只存储在叶子结点上。非叶子结点只存储索引。
在上面的具体规定中,M 和 L 是如何确定的呢?
M 和 L的确定与磁盘块的大小相关。对于B树而言,每个结点都尽量占据一个磁盘块。
比如,假设有 1千万数据项,每个关键字(索引)是32B,而每个数据项是256B,磁盘块的大小是8192B,如何确定M 和 L 呢?
由于M阶B树中,每个结点最多有 M-1 个关键字,故关键字总大小为 32M-32,M-1个关键字最多有M个分支指针,假设每个分支指针是4B(字节),故分支指针
的大小是4*M个字节。那么对于一个非叶子结点,它的大小是36*M-32 字节,由于磁盘块大小是8192,故M = 8192/(36*M-32) = 228
由上面的第5点可知,叶子结点只存储数据项,每个数据项大小为256B,故 L=8192/256=32,这说明每个叶子结点可以存储32个数据项。
M 与关键字以及指针的大小有关,而L与数据项的大小有关。总之,目标是:不管是叶结点还是非叶结点,都尽量保证一个结点占据一个磁盘块。
二,B树的基本操作
1)查找操作
查找操作的伪代码如下:《算法导论》这里的B树中数据项可存储在非叶子结点上。
1 B-TREE-SEARCH(x,k) 2 i = 1 3 while i<= M‘ and k > key(i) 4 i++ 5 if i<=M‘ and k=key(i) 6 return (x,i) 7 if leaf(x) 8 return NIL 9 else 10 DISK-READ(child(x(i))) 11 return B-TREE-SEARCH(child(x(i)),k)
x实际上代表根结点。第3行,扫描结点上所有的数据项看是否与k匹配,若不匹配且结点不是叶子结点,则需要在第10行进行一次磁盘读取操作,将该结点中某数据项指向的孩子结点读入内存,再进行比较。
2)插入操作
插入操作可能会导致结点分裂。插入操作的具体实现细节可能与这里描述的不一样。
比如,向一个已经满了的叶结点插入一个数据项时,该叶结点分裂成两个结点,并将中间数据项上移到该结点的双亲结点。
3)删除操作
删除操作可能会导致结点合并。具体描述参考算法导论。
比如,还可以这样来处理:当某个节点不包含的数据项已经达到最小时,可以从邻节点 “领养” 一个数据项。当邻节点也不足时,则将这两个节点合并成一个节点。
三,B树与B+树的主要区别
最主要的区别就是:B树中非叶子结点可以存储数据,而B+树非叶子结点只存储索引,所有的数据都放在叶子结点上存储。
参考:B树学习总结
标签:
原文地址:http://www.cnblogs.com/hapjin/p/5616927.html