数据结构之B-树

搜索树(Search Tree)是一个很重要的数据结构。
搜索树包括:二叉搜索树、平衡树、红黑树、B-树。

今天我们的主角是B-树。在大数据时代,B-树成为一个非常热的数据结构。因为搜索树是进行数据查找非常有效的数据结构,而二叉树、平衡树、红黑树他们都是把要查找的字典放在内存中。对于较大大的字典,内存放不下,只能存放在磁盘中,我们的B-树就派上用场了。

先介绍一下在磁盘中的数据的组织和访问方法:索引顺序访问(Indexed Sequential Access Method,即ISAM)。磁盘被分成许多块,如果你不能区分磁盘块、扇区…请点击我。字典元素在一个块中是按升序的方式进行排列的。当我们的字典非常大时,就会占据许多块。那我们就为这些块建立磁盘块索引,索引记录块中的最大元素,索引如果不是很大,可以直接放在内存中。但是,索引若是很大,我们就要为索引建立索引。这样多级索引便出现了。但是,访问起来虽然会比较快,但是,你要是想插入数据,就比较麻烦了。于是,我们在每一个块中留下适当的空间来避免数据在块与块之间的移动。

我们在建立多级索引的时候,就会出现一种数据结构 m叉搜索树(m-way search tree)。给出m叉搜索树的定义:
1.每一个节点最多有m-1个元素和m个孩子
2.元素的值是从小到大的
3.节点中的元素值大于所有此元素的左孩子值,小于所有此元素的右孩子的值
4.空节点使用外部节点来表示(实际中使用0指针来表示空节点)

一棵7叉树:
《数据结构之B-树》
7叉树的搜索
当我们搜索20时,从根节点开始,判断大比10大比80小,那他就一定会出现在10的右孩子中。进入10的右孩子,在右树中寻找到20,返回值。
当搜索32时,还是会进入10的右孩子进行搜索,发现比30大比40小,于是进入30的右孩子进行搜索。搜索到32,返回值。
当搜索31时,还是会进入10的右孩子,进入30的右孩子,搜索发现,比32小,于是进入32的左孩子,但左孩子是外部节点,于是,返回未搜索到。

综上,我们发现,搜索的次数最多就是树的高度h。即,我们最多要进行h次磁盘的访问。因为,读取磁盘的速度远远小于读取内存的速度,所以内存的访问忽略不计。也就是说,我们的时间复杂度是O(h)

7叉树的插入
先在我们的书中进行查找,若是找到相同的关键字,拒绝插入。没有相同关键字时,在最后查找的地方进行做文章。若最后的节点还没有满,那我们就把新元素放在这个节点处。若最后的节点满了,我们就要在这个节点中相应的元素处加上孩子。

在上面给出的7叉树中添加20,31,65时。
搜索20,找到20,拒绝。
搜索31,停在[32][36]处,这个节点没有满,直接把新元素放进来。
搜索65,停在[20][30][40][50][60][70]处,这个节点已经满了,我们只能把他添加到70的左孩子上。

《数据结构之B-树》

分析一下,最坏是要插在最下一层的下一层。于是,有h次的磁盘访问,和最后一次的写回,磁盘的访写次数是h+1。于是,时间复杂度是O(h)

7叉树的删除
删除时,要考虑删除节点是不是存在,不存在拒绝。当存在时,若元素既没有左孩子也没有右孩子,则我们可以直接进行删除。若元素有一个孩子,则从这个孩纸中找出合适(左找最大,右找最小)的元素来代替他,然后,把这个合适的元素从原先的位置上进行删除。当有两个孩子时,我们可以让他选择左孩子,与上面的一个孩子类似。

还是最开始的那棵树来举例。
《数据结构之B-树》
1)我们删除100,没找到,拒绝。
2)删除90时,90没有孩子,直接删除。
3)删除88时,88只有右孩子,从右孩子中找最小的元素90,用90替换88。删除原来的90=>2)
4)删除10时,默认从左孩子中找代替他的,找到5,替换10;删除5=>找5左孩子中最大的4,替换5=>删除4,4没有孩子,直接删除。

分析一下,最复杂的情况就是,我们删除的元素在根节点,而且,这次的删除会影响到叶子。于是,从根到叶子节点,每个节点被访问一次、被修改一次,进行2h次访写时间复杂度是O(h)

《数据结构之B-树》

===============================================
综上,我们可以看到,时间的复杂度总是和h有关系。那么,有n个节点的m叉树,最多有多高?最少有多高?

当每一层有一个节点、且每一个节点只有一个元素时,树最高。max(h)=n;
当每一层的每一个节点都有m个元素(放满)时,树最矮。min(h)= logm(n+1) ;

于是,树的高度就在[ logm(n+1) ,n]。为了操作的性能,我们想办法让高度尽量接近 logm(n+1) 。于是,B-树就出现了。

===============================================

简单的定义一下B-树,(m序B-树,B-Tree of order m):
1.首先,他是一棵m叉搜索树。
2.根节点至少有两个孩子。
3.除根以外的内部节点必须至少有 m/2 个孩子。
4.所有的外部节点都位于同一层上。

那么,经过这种限制以后,树的高度又是多少?
记d= m/2
树最高的情况就是,树刚好满足B-树的定义,节点数n= 2(dh11)+1=2dh11 。于是,h= logd((n+1)/2)+1
树最矮的情况就是,还是和上面的一样,每一个节点都放满。于是,此时h= logm(n+1)

B-树的序m越大,d越大,h也就越小。但是,这样真的就会越快吗?答案是 不是的。当我们的节点大小超过一个块时,访问一个节点就会访问多个磁盘块。所以,我们的序m要根据磁盘块的大小和元素的大小合理确定。

B-树的搜索和上面m叉树的搜索一样

B-树的插入
当我们插入时,先搜索,若搜到了,那么,拒绝插入。
没搜到,在最后一次访问的节点处判断,如果这个节点没有满,就把这个新元素放在这个节点上。如果这个节点满了,那么就需要进行分裂(把新元素和节点上的元素放在一起,将前半部分变成一个节点,后半部分变成一个节点,中间的元素向上插入(递归)。以 m/2 为中)。

一棵7序B-树:
《数据结构之B-树》

1)插入2,搜到,拒绝。
2)插入3,最后节点是[2][4][6],没满,插入。
3)插入21,最后节点是[20][30][40][50][60][70],满了,进行分裂。
[20][21][30][40][50][60][70]的中间是第4个即[40]。把[40]前面的变成一个节点,[40]后面的变成一个节点。[40]向父节点中进行插入。结果如下:

《数据结构之B-树》

分析一下,最复杂就是,插入的节点在叶上、而且会发生分裂、且分裂到根。于是,有h次的访问,2h次的节点分裂,1次的新根节点写回。共有3h-1次的磁盘访写。于是,时间复杂是O(h)

B-树的删除
这时候分的情况就比较多了。
首先,要找这个元素是不是存在,不存在,拒绝。
当这个元素在叶子上时,判断叶节点删掉后,元素数是不是小于 m/2 。不小于,直接删掉。小于时,判断左右兄弟是不是可以帮他。可以帮,“曲线救兄法”支援给这个节点一个元素。不可以帮,“兄弟齐心法”兄弟联合,拉下父元素来,让父元素替掉要删的元素,删掉父元素(递归)。
当这个元素位于内部时,让右孩子的最大元素来代替他,删掉右孩的最大元素。(递归)

对下面的B-树进行删除
《数据结构之B-树》
七序B-树
1)当删除1时,找不到,拒绝。
2)当删除2时,找到,删掉后仍符合B-树,直接删掉。
3)当删除20时,删掉后不符合B-树,不能直接删掉,看看他的兄弟。啊,左兄弟元素多可以支援给我。于是曲线救兄。
《数据结构之B-树》

换一棵树来讲另一种情况
《数据结构之B-树》
这是一棵3序B-树。

4)当删除10时,10拿走,20和25合并成一个节点,删掉原来的20,于是,判断右兄弟可以帮删20,于是,再来一个“曲线救兄”。流程图:
《数据结构之B-树》

再换一棵树来讲另一种情况
《数据结构之B-树》
5)删除44时,[35]和[40]合并,删掉原来的[40]=>于是,[20][30]合并,删掉原来的[30]=>于是,[50][80]合并。删掉原来的[50]。[50]可直接删掉,结束。
《数据结构之B-树》
《数据结构之B-树》
《数据结构之B-树》

分析一下,操作次数:
1.找到节点:h次
2.获取2至h最相邻兄弟:h-1次
3.在第3至h层进行合并:h-2次
4.修改根节点和第二层的两个节点:3次
总操作次数:3h次

当删除内节点时,使用递归,将其转换成叶节点的删除即可。

总结一下:
《数据结构之B-树》

    原文作者:B树
    原文地址: https://blog.csdn.net/a1459268562/article/details/53486694
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞