B树、B+树、B*树

不念不忘少年蓝@ 2022-06-05 05:14 482阅读 0赞

B树

B树是一种平衡的多路查找树
定义:一棵m 阶的B树,或者为空树,或为满足下列特性的m 叉树:
1 树中每个结点至多有m个孩子;
2 除根结点和叶子结点外,其它每个结点至少有m/2个孩子;
3 若根结点不是叶子结点,则至少有2个孩子;
4 所有叶子结点都出现在同一层,叶子结点不包含任何关键字信息;
5 有k个孩子的非终端结点包含k-1个关键字

B树实现、插入相关代码:

  1. #pragma once
  2. template<class K,class V,size_t M>
  3. struct BTreeNode
  4. {
  5. pair<K,V> _kvs[M];//多开一个空间 ,方便分裂
  6. BTreeNode<K,V,M> * _sub[M+1];
  7. BTreeNode<K,V,M> * _parent;
  8. size_t _size; // 关键字的数量
  9. BTreeNode()
  10. :_parent(NULL)
  11. ,_size(0)
  12. {
  13. for (size_t i = 0;i < M;++i)
  14. {
  15. _sub[i] = NULL;
  16. }
  17. }
  18. };
  19. template<class K,class V,size_t M>
  20. class BTree
  21. {
  22. typedef BTreeNode<K,V,M> Node;
  23. public:
  24. BTree()
  25. :_root(NULL)
  26. {}
  27. pair<Node*,int> Find(const K& key)//寻找
  28. {
  29. Node *cur = _root;
  30. Node * parent = NULL;
  31. while (cur)
  32. {
  33. size_t i = 0;
  34. while (i<cur->_size)
  35. {
  36. if (cur->_kvs[i].first > key)//在i的左树
  37. {
  38. break;
  39. }
  40. else if (cur->_kvs[i].first <key)
  41. {
  42. ++i;
  43. }
  44. else
  45. {
  46. return make_pair(cur,i);
  47. }
  48. }
  49. parent = cur;
  50. cur = cur->_sub[i];
  51. }
  52. return make_pair(parent,-1);
  53. }
  54. bool Insert(const pair<K,V>& kv)
  55. {
  56. if (_root == NULL)
  57. {
  58. _root = new Node;
  59. _root->_kvs[0] = kv;
  60. _root->_size = 1;
  61. return true;
  62. }
  63. pair<Node*,int> ret = Find(kv.first);
  64. if (ret.second >= 0)
  65. {
  66. return false;
  67. }
  68. Node* cur = ret.first;
  69. pair<K,V> newKV = kv;
  70. Node* sub = NULL;
  71. while (1)
  72. {
  73. //往cur插入newKV,sub InsertKV(cur,newKV,sub);
  74. if (cur->_size < M)
  75. {
  76. return true;
  77. }
  78. else //分裂
  79. {
  80. Node* newNode = Divide(cur);
  81. pair<K,V> midKV = cur->_kvs[cur->_size/2];
  82. cur->_size -= (newNode->_size+1);
  83. //1.根节点分裂
  84. if (cur == _root)
  85. {
  86. _root = new Node;
  87. _root->_kvs[0] = midKV;
  88. _root->_size = 1;
  89. _root->_sub[0] = cur;
  90. _root->_sub[1] = newNode;
  91. cur->_parent = _root;
  92. newNode->_parent = _root;
  93. return true;
  94. }
  95. else
  96. {
  97. cur = cur->_parent;
  98. newKV = midKV;
  99. sub = newNode;
  100. }
  101. }
  102. }
  103. }
  104. Node* Divide(Node *cur)
  105. {
  106. Node * newNode = new Node;
  107. int mid = cur->_size/2;
  108. size_t j = 0;
  109. size_t i = mid+1;
  110. for (;i < cur->_size;++i)
  111. {
  112. newNode->_kvs[j] = cur->_kvs[i];
  113. newNode->_sub[j] = cur->_sub[i];
  114. if(newNode->_sub[j])
  115. newNode->_sub[j]->_parent = newNode;
  116. newNode->_size++;
  117. j++;
  118. }
  119. newNode->_sub[j] = cur->_sub[i];
  120. if(newNode->_sub[j])
  121. newNode->_sub[j]->_parent = newNode;
  122. return newNode;
  123. }
  124. void InsertKV(Node* cur,const pair<K,V> &kv,Node* sub) {
  125. int end = cur->_size-1;
  126. while (end >= 0)
  127. {
  128. if (cur->_kvs[end].first > kv.first)
  129. {
  130. cur->_kvs[end+1] = cur->_kvs[end];
  131. cur->_sub[end+2] = cur->_sub[end+1];
  132. --end;
  133. }
  134. else
  135. {
  136. break;
  137. }
  138. }
  139. cur->_kvs[end+1] = kv;
  140. cur->_sub[end+2] = sub;
  141. if(sub) sub->_parent = cur;
  142. cur->_size++;
  143. }
  144. void InOrder()
  145. {
  146. _InOrder(_root);
  147. cout<<endl;
  148. }
  149. void _InOrder(Node *root)
  150. {
  151. if (root == NULL)
  152. {
  153. return;
  154. }
  155. Node * cur = root;
  156. size_t i = 0;
  157. for (;i < cur->_size;++i)
  158. {
  159. _InOrder(cur->_sub[i]);
  160. cout<<cur->_kvs[i].first<<" ";
  161. }
  162. _InOrder(cur->_sub[i]);
  163. }
  164. private:
  165. Node* _root;
  166. };
  167. void TestBTree()
  168. {
  169. int a[]={
  170. 53,75,139,49,145,36,101};
  171. BTree<int,int,3> b;
  172. for (size_t i = 0;i<sizeof(a)/sizeof(a[0]);++i)
  173. {
  174. b.Insert(make_pair(a[i],i));
  175. }
  176. b.InOrder();
  177. }

B+树

B+树的特点与B树的区别

这里写图片描述

这里写图片描述

  • 内部节点中,关键字的个数与其子树的个数相同,不像B树种,子树的个数总比关键字个数多1个
  • 所有指向文件的关键字及其指针都在叶子节点中,不像B树,有的指向文件的关键字是在内部节点中。换句话说,B+树中,内部节点仅仅起到索引的作用,
  • 在搜索过程中,如果查询和内部节点的关键字一致,那么搜索过程不停止,而是继续向下搜索这个分支
  • B+树的分裂:当一个结点满时,分配一个新的结点,并将原结点中1/2的数据复制到新结点,最后在父结点中增加新结点的指针;B+树的分裂只影响原结点和父结点,而不会影响兄弟结点,所以它不需要指向兄弟的指针。

根据B+树的结构,我们可以发现B+树相比于B树,在文件系统,数据库系统当中,更有优势,原因如下:

索引作为索引文件存在磁盘里

  • B+树的磁盘读写代价更低
    B+树的内部结点并没有指向关键字具体信息的指针。因此其内部结点相对B树更小。如果把所有同一内部结点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多。一次性读入内存中的需要查找的关键字也就越多。相对来说I/O读写次数也就降低了。
  • B+树的查询效率更加稳定
    由于内部结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。
  • B+树更有利于对数据库的扫描
    B树在提高了磁盘IO性能的同时并没有解决元素遍历的效率低下的问题,而B+树只需要遍历叶子节点就可以解决对全部关键字信息的扫描,所以对于数据库中频繁使用的range query,B+树有着更高的性能。

B*树

B*树的特点

  • B*树是为了节省空间而提出的,B和B+树中若每个节点最多能存放M个关键字,则最少需要(M-1)/2个关键字,而B*树能够更加节省空间,最少是(2/3)M个关键字,因此更能够有效的利用空间,但是插入动作会更加麻烦,即块的最低使用率为2/3(代替B+树的1/2)。
  • B*树的分裂:当一个结点满时,如果它的下一个兄弟结点未满,那么将一部分数据移到兄弟结点中,再在原结点插入关键字,最后修改父结点中兄弟结点的关键字(因为兄弟结点的关键字范围改变了);如果兄弟也满了,则在原结点与兄弟结点之间增加新结点,并各复制1/3的数据到新结点,最后在父结点增加新结点的指针。
    所以,B*树分配新结点的概率比B+树要低,空间使用率更高;

发表评论

表情:
评论列表 (有 0 条评论,482人围观)

还没有评论,来说两句吧...

相关阅读

    相关 ---BB+B*

    B树 B树(也称B-tree,B-树)特点: B树的阶:节点的最多子节点个数 B-树的搜索,从根结点开始,对结点内的关键字(有序)序列进行二分查找,如果命中

    相关 BB-B+B*

    B树 即二叉搜索树: 1.所有非叶子结点至多拥有两个儿子(Left和Right); 2.所有结点存储一个关键字; 3.非叶子结点的左指针指向小于其关键字的子树,右

    相关 BB+B*

    简介       [B-tree][]树即[B树][B],B即Balanced,平衡的意思。因为B树的原英文名称为B-tree,而国内很多人喜欢把B-tree译作B-树,