多路平衡树—BTree(B树)

B树属于多叉树,也称多路平衡树。有些地方也将B树称为‘B-树‘,这里‘-’不表示减号。

■B树的主要性质

(1)根节点至少有两个孩子。

(2)每个非根节点为[[M/2], M]个孩子,这里[M/2]表示向上取整。

(3)每个非根节点都有[[M/2], M-1]个关键字,并且以升序排列。

(4)K[i]和k[i+1]之间的孩子节点的值介于k[i]与k[i+1]之间。(5)所有叶子节点都在同一层。

■下面是一个简单的3阶B树:

如果想给B树中,插入一个关键字,并且关键字的数目超过,且就需要对树进行调整。那就需要寻找关键字的中位数,那怎样快速的寻找关键字呢?

▲思路一:

将所有的关键字进行排序,然后将中位数寻找出来。

▲思路二:

利用快速排序的思想,选一个key值,如果左边个数等于右边个数,则中位数找到,如果没有,就在个数多的一边找出中间位置的关键字作为key值,直到key的左 = 右,则找到关键字,这样的效率更高。

■下面是插入关键字示例:

■下面是具体的实现代码:

#pragma once
//实现B树(实际就是多叉树)

/*
性质:(1)根节点至少要2个节点
      (2)每个非根节点为[(M/2), M]个孩子
   (3)满足左孩子值小于根节点,右孩子值大于根节点
   (4)并且每个非根节点有[(M/2)-1, M-1]个关键字,并且以升序排列
   (5)key[i]和key[i+1]之间的孩子节点值介于key[i]和key[i+1]之间
   (6)所有节点都在同一层
*/

//实现k形式的结构
//如果要实现K,V结构,就需要创建一个结构体,包括K,V
template <class K, int M = 3>   //实现M为缺省的,值最好取计数,能够更加方便的求取中位数
struct BTreeNode
{
     K _keys[M];      //关键字的至多个数,多预留一个位置是可以更加方便的求取中位数
     BTreeNode<K, M>* _subs[M + 1];      //孩子节点的最大数目
     BTreeNode<K, M>* _parent;    //指向父亲节点
     size_t _size;     //数组中存在的有效关键字的个数
     
     BTreeNode()           //构造B树节点
          :_parent(NULL)
          , _size(0)
     {
          for (int i = 0; i <= M; ++i)
          {
               _subs[i] = NULL;
          }
     }
};

template <class K, class V>    //需要返回两个参数,使用结构体
struct Pair
{
     K _first;
     V _second;
     
     Pair(const K& key = K(), const V& value = V())     //缺省参数,会调用默认构造函数
          :_first(key)
          , _second(value)
     { }
};

template <class K, int M = 3>
class BTree
{
     typedef BTreeNode<K, M> Node;
public:
     BTree()          //无参构造
          :_root(NULL)
     {}
     
     Pair<Node*, int>  Find(const K& key)      //查找
     {
          Node* parent = NULL;
          Node* cur = _root;
          while (cur)
          {
               int index = 0;
               while (index < cur->_size)     //在一个节点中找相同的关键字
               {
                    if (key == cur->_keys[index])
                    {
                         return Pair<Node*, int>(cur, index);
                    }
                    else if (key < cur->_keys[index])
                    {
                         break;
                    }
                    else
                    {
                         index++;
                    }
               }
               parent = cur;
               cur = cur->_subs[index];
          }
          return Pair<Node*, int>(parent, -1);
     }
     
     bool Insert(const K& key)     //插入节点
     {
          //没有节点
          if (_root == NULL)
          {
               _root = new Node;
               _root->_keys[0] = key;
               _root->_size++;
               return true;
          }
          
          //判断返回值
          Pair<Node*, int> cur = Find(key);
          if (cur._second != -1)
          {
               return false;
          }
          
          //在节点cur中插入key和sub
          Node* str = cur._first;
          K InsertKey = key;
          Node* sub = NULL;
          while (1)
          {
               _InsertKey(str, InsertKey, sub);    
               if (str->_size < M)    //插入后,节点中的数据个数没有超过规定的
               {
                    return true;
               }
               //插入数据后,节点的数据个数大于规定的数据个数,需要将节点进行分裂
               int mid = (str->_size - 1) / 2;
               int index = 0;
               Node* tmp = new Node;
               
               //先拷贝key
               for (int i = mid + 1; i < str->_size; i++)
               {
                    tmp->_keys[index++] = str->_keys[i];
                    tmp->_size++;
               }
               
               //后拷贝sub
               for (int i = mid + 1; i < str->_size; i++)
               {
                    tmp->_subs[index + 1] = str->_subs[i];
                    if (str->_subs[i])
                    {
                         str->_subs[i]->_parent = tmp;
                    }
               }
               str->_size = (str->_size - 1) / 2;    //更改str的大小
               if (str->_parent == NULL)
               {
                    _root = new Node;
                    _root->_keys[0] = tmp->_keys[mid];
                    _root->_subs[0] = str;
                    _root->_subs[1] = tmp;
                    _root->_size = 1;
                    str->_parent = _root;
                    tmp->_parent = _root;
               }
               else
               {
                    InsertKey = str->_keys[mid];
                    sub = tmp;
                    str = str->_parent;
               }
          }
          return true;
     }
     
     void _InsertKey(Node* cur, const K& key, Node* sub)     //插入key值
     {
          int index = cur->_size - 1;
          while (index >= 0 && cur->_keys[index] > key)    //将后面的数据向后移一位
          {
               cur->_keys[index + 1] = cur->_keys[index];
               cur->_subs[index + 2] = cur->_subs[index + 1];
               --index;
          }
          cur->_keys[index + 1] = key;    //插入数据及其子节点
          cur->_subs[index + 2] = sub;
          if (sub)
               sub->_parent = cur;
          cur->_size++;
     }
     
     void InOrder()
     {
          _InOrder(_root);
     }
     
     void _InOrder(Node* root)
     {
          if (root == NULL)
          {
               return;
          }
          for (int i = 0; i < root->_size; i++)
          {
               cout << root->_keys[i] << " ";
               _InOrder(root->_subs[i]);
          }
     }
 
protected:
     Node* _root;
};

void Test()
{
     int a[] = { 53, 75, 139, 49, 145, 36, 101 };
     BTree<int, 1023> t;
     for (size_t i = 0; i < sizeof(a) / sizeof(a[0]); ++i)
     {
          t.Insert(a[i]);
     }
     t.InOrder();
}
时间: 2024-08-27 22:35:25

多路平衡树—BTree(B树)的相关文章

自己写的java实现的多路搜索树 B-Tree

最近需要写范围查询的功能,最简单的应该是B+树吧,在了解B+树的时候,也看到了B-树.于是想先实现B-Tree再实现B+Tree,结果网上并没有找到B-Tree(多路搜索树),于是自己用java实现了一个,经过自己设计了很多测试用例,用Junit(临时学的)测试可用.在这里贴出来,希望能给初学者一点参考,也希望能有高人指点可以改进的地方,欢迎讨论批评指点!自己之前一直在做工程,这是一年多来首次写数据结构,自己还很弱,欢迎大家批评指正!!! 本B-Tree理论部分参考博客:http://blog.

B树、B-tree B+树、B*树

BST 即二叉搜索树: 1.所有非叶子结点至多拥有两个儿子(Left和Right): 2.所有结点存储一个关键字: 3.非叶子结点的左指针指向小于其关键字的子树,右指针指向大于其关键字的子树: 如: B-树(B树) 是一种多路搜索树(并不是二叉的): 1.定义任意非叶子结点最多只有M个儿子:且M>2: 2.根结点的儿子数为[2, M]: 3.除根结点以外的非叶子结点的儿子数为[M/2, M]: 4.每个结点存放至少M/2-1(取上整)和至多M-1个关键字:(至少2个关键字) 5.非叶子结点的关键

【转】B树、B-tree B+树、B*树

原文链接 http://www.linuxidc.com/Linux/2014-11/109103.htm BST 即二叉搜索树: 1.所有非叶子结点至多拥有两个儿子(Left和Right): 2.所有结点存储一个关键字: 3.非叶子结点的左指针指向小于其关键字的子树,右指针指向大于其关键字的子树: 如: B-树(B树) 是一种多路搜索树(并不是二叉的): 1.定义任意非叶子结点最多只有M个儿子:且M>2: 2.根结点的儿子数为[2, M]: 3.除根结点以外的非叶子结点的儿子数为[M/2, M

bzoj 3196/ Tyvj 1730 二逼平衡树 (线段树套平衡树)

3196: Tyvj 1730 二逼平衡树 Time Limit: 10 Sec  Memory Limit: 128 MB[Submit][Status][Discuss] Description 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:1.查询k在区间内的排名2.查询区间内排名为k的值3.修改某一位值上的数值4.查询k在区间内的前驱(前驱定义为小于x,且最大的数)5.查询k在区间内的后继(后继定义为大于x,且最小的数) Input 第一行两个数 n,

BZOJ3196 二逼平衡树 ZKW线段树套vector(滑稽)

我实在是不想再打一遍树状数组套替罪羊树了... 然后在普通平衡树瞎逛的时候找到了以前看过vector题解 于是我想:为啥不把平衡树换成vector呢??? 然后我又去学了一下ZKW线段树 就用ZKW线段树套vector水过啦!!! 每个ZKW线段树的节点保存一个vector 操作1在分出的vector上查询比它小的数有多少个然后相加再加1 操作2二分再上操作1 操作3修改需要修改的节点的vector 操作4在分出vector上查询前驱取最大 操作5与操作4同理 luogu主站5772ms上卡过,

BZOJ3196 二逼平衡树 【线段树套平衡树】

题目 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作: 1.查询k在区间内的排名 2.查询区间内排名为k的值 3.修改某一位值上的数值 4.查询k在区间内的前驱(前驱定义为小于x,且最大的数) 5.查询k在区间内的后继(后继定义为大于x,且最小的数) 输入格式 第一行两个数 n,m 表示长度为n的有序序列和m个操作 第二行有n个数,表示有序序列 下面有m行,opt表示操作标号 若opt=1 则为操作1,之后有三个数l,r,k 表示查询k在区间[l,r]的排名 若

[bzoj3065] 带插入区间第k小值 [重量平衡树套线段树]

题面 传送门 思路 发现强制在线了...... 本来可以树套树解决的问题,现在外层不能使用线段树了,拿什么替代呢? 我们需要一种支持单点插入.下套数据结构.数据结构上传合并复杂度最多单log,不能旋转的数据结构 这不是摆明了用重量平衡树吗? 我选了替罪羊树作为上层结构,下面套了一棵线段树,就做完了 查询的时候把替罪羊树上对应的log个区间提取出来,一起在底层权值线段树上二分即可 详见代码注释 Code #include<iostream> #include<cstdio> #inc

java项目---用java实现二叉平衡树(AVL树)并打印结果(详)

1 package Demo; 2 3 public class AVLtree { 4 private Node root; //首先定义根节点 5 6 private static class Node{ //定义Node指针参数 7 private int key; //节点 8 private int balance; //平衡值 9 private int height; //树的高度 10 private Node left; //左节点 11 private Node right;

可持久化数据结构(平衡树、trie树、线段树) 总结

然而好像没有平衡树 还是题解包: T1:森林 树上主席树+启发式合并. 然而好像知道标签就没啥了.在启发式合并时可以顺手求lca 然而这题好像可以时间换空间(回收空间) T2:影魔 难点在于考虑贡献的来源 考虑一个区间两端点和区间最值(不含端点)的关系 小,中,大:贡献p1 大,小,大:贡献p2 大,中,小:贡献p1 则预处理出每个点左右第一个比它大的数的位置,设为l和r 则l会对r有p2的贡献,l会对i+1-r-1产生p1的贡献,同理r会对l+1~i-1产生p1的贡献. 用线段树维护扫描线,正