B+树

为什么说B+-tree比B 树更适合实际应用中操作系统的文件索引和数据库索引?

1) B+-tree的磁盘读写代价更低 
B+-tree的内部结点并没有指向关键字具体信息的指针。因此其内部结点相对B 树更小。如果把所有同一内部结点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多。一次性读入内存中的需要查找的关键字也就越多。相对来说IO读写次数也就降低了。 
举个例子,假设磁盘中的一个盘块容纳16bytes,而一个关键字2bytes,一个关键字具体信息指针2bytes。一棵9阶B-tree(一个结点最多8个关键字)的内部结点需要2个盘快。而B+ 树内部结点只需要1个盘快。当需要把内部结点读入内存中的时候,B 树就比B+ 树多一次盘块查找时间(在磁盘中就是盘片旋转的时间)。
看节点定义可以看出来。
2) B+-tree的查询效率更加稳定
由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。
3)B+树还有一个最大的好处,方便扫库,B树必须用中序遍历的方法按序扫库,而B+树直接从叶子结点挨个扫一遍就完了,B+树支持range-query非常方便,而B树不支持。这是数据库选用B+树的最主要原因。

“有很多基于频率的搜索是选用B树,越频繁query的结点越往根上走,前提是需要对query做统计,而且要对key做一些变化。”

B*-tree:
B*-tree是B+-tree的变体,在B+ 树非根和非叶子结点再增加指向兄弟的指针;B*树定义了非叶子结点关键字个数至少为(2/3)*M,即块的最低使用率为2/3(代替B+树的1/2)。


B-tree:
定义:m阶,子女节点个数从ceil(m/2)到m/2,关键字个数从ceil(m/2)-1到m-1,比子女节点个数刚好少1.
/**
 *  插入关键字伪代码:
	1、找到应该插入位置的节点,一定是叶子节点,直接插入;
	2、如果该叶子节点关键字个数大于m-1;分裂该叶子节点;

	分裂节点伪代码:
	1、分裂该节点,产生一个新节点;
	2、将中间关键字插入父节点中;
	3、如果父节点关键字个数大于m-1,递归分裂父节点,否则直接返回;

 *  如果name已经存在,返回false
 **/
/**
 *  删除关键字伪代码----最终是在叶子节点上删除数据;
    1、查找包含这个关键字key的节点node:
	2、如果这个节点是内节点:
		a 找到左子树中含有最大关键字的节点leafnode,及其最大的关键字keyx;
	    b 在叶子节点leafnode上删除关键字keyx,并用keyx代替原来将要删除的关键字key
		c 维护leafnode
	   如果这个节点是叶子节点:
	    a 直接删除这个关键字,移动后面的其他关键字
		b 维护这个节点

	维护节点伪代码:
		a 如果关键字满足要求,直接返回;
		b 如果左右兄弟节点有足够多的关键字,向其借一个,返回;
		c 如果左右兄弟节点都没有足够的关键字,合并一个兄弟节点,回溯维护父节点。
 *
 *  如果name已经存在,返回false
 **/

B+tree:
定义:内部节点全部是索引关键字,data都在叶子节点。
内部节点:子女节点个数为ceil[m/2]到m,关键字可以和子女节点对应也可以少1(不同的书有不同的说法).关键字i是子树i+1的最小关键字。
叶子节点:全部是key-value值,个数ceil(L/2)-->L( L << M in practice)。
根节点:单个或是2-->M个子节点。

实际中上:
每个节点通常占用一个I/O块;
1/2级节点常驻内存;
大多数情况很多内部节点的关键字个数少于(m-1),对内存的极大浪费。

各大IT公司面试常常问到的问题——海量数据问题。以前通常回答二级索引,即一级索引常驻内存,通过一级索引找到二级索引,读入内存,再通过二级索引找到最终要找的具体数据,而“索引”,一直设想的都是HASH,现在回头想来,HASH其实是不合适的。因为HASH只能提供映射,而不能提供范围信息。这个问题的正确答案应该是B树或者B+树。 

Insert()
/**
    1、找到叶子节点,直接插入;
    2、>L, 则SplitLeafNode 或者是 SplitRootNode()

    SplitLeafNode()
    1 Node ==> LeftNode( ceil(L/2) ) + Right( remaining );
    2 向父节点插入rightNode和right节点中最小的关键字 InsertInternalNode(key, rightNode)
    
    InsertInternalNode()
    1 直接插入关键字及其右子树;
    2 子女节点个数>M,则 SplitInternalNode()

    SplitInternalNode()
    1 Node ==> Left( ceil(M/2)-1 ) + Key(中间) + Right( M/2 )
    2 InsertInternalNode( Key, Right )
 **/
Delete()
/**
    感觉挺麻烦的,细节还是不太清楚
    1、找到叶子节点,删除,如果有内部节点包含这个关键字,则用叶子节点的最小值替换
    2、关键字个数小于
    1、如果LeftSibling关键字个数>=ceil(L/2)+1,LendLeftSibling;
    2、LendRightSibling
    3、MergeLeafNode() or MergeInternalNode()
    
    MergeLeafNode: 注意parent的key只需要删除就可以了
    MergeInternalNode:注意parent的key要往下移动
 **/

参考:http://blog.csdn.net/v_JULY_v/article/details/6530142
http://en.wikipedia.org/wiki/B%2Btree
一份B+树ppt,很好,http://www.albertsong.com/read-174.html


你可能感兴趣的:(学习算法)