为什么说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、关键字个数小于<L:Rebalance();
Rebalance()
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