B-树

来源:互联网 发布:java环境变量一键配置 编辑:程序博客网 时间:2024/05/16 08:15

           B-树是一种平衡的多叉树,称为B树。(有些地方写的是B-树,注意不要误读 成"B减树") 一棵M阶(M>2)的B树,是一棵平衡 的 M路平衡搜索树,可以是空树或者满足一下性质



        
1. 根节点至少有两个孩子

        2. 每个非根节点有[ M/2,M]个孩子

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

        4. key[i]和key[i+1]之间的孩子节点的值介于key[i]、key[i+1]之间

       5. 所有的叶子节点都在同一层


       ps[M/2]: 是向上取整


        这里以最简单的M阶B树--M=3为例:实现元素的插入,查找以及遍历,当然插入元素是有点难度的,既然是平衡搜索树,中序遍历出来就是有序的

  首先Btree节点定义:

 

template <class K,int M>struct BtreeNode{K _key[M];//关键字数组,多一个便于分裂BtreeNode<K,M> * _parent;BtreeNode<K,M> * _sub[M+1];//指向子树的指针数组int _size;BtreeNode():_size(0),_parent(NULL){    for(int i=0;i<M;i++){_key[i] = K();_sub[i] = NULL;}_sub[M] = NULL;}};

元素的插入:

以插入 int arr[]={53, 75, 139, 49, 145, 36, 101};为例

首先插入53,75,139后插入49,14两个个简单的元素

然后再插入36,就不符合<M,就需进行分裂

分裂之后,再进行插入101,插入101,就需要进行二次分裂了


  查找指定元素:

pair <Node *,int> Find(const K & key){Node *cur = _root;Node *parent = NULL;while(cur){int i=0;while(i<cur->_size){  if(cur->_key[i]<key)  {++i;  }  else if(cur->_key[i]>key) {break; } else {return pair<Node *, int>(cur,i);//找到返回当前位置 }}parent = cur;cur = cur->_sub[i];}return pair<Node *,int>(parent,-1);//未找到返回-1}

插入指定元素

void InsertKey(Node * node,const K & key,Node * sub){int i = node->_size-1;while(i >= 0){if(node->_key[i] > key){break;}else {//将node[]值循环向后拷贝,空出第一位,同时将相应孩子,也带走node->_key[i+1] = node->_key[i];node->_sub[i+2] = node->_sub[i+1];                --i;} }//循环出来后,指针将指向待插入位置,同时孩子指针也指向相应位置node->_key[i+1] = key;node->_sub[i+2] = sub;if(sub){sub->_parent = node;}node->_size++;}

插入指定元素,并进行向上调整:

bool Insert(const K & key){if(_root == NULL)//根节点为空,创建节点插入key{_root = new Node;_root->_key[0] = key;_root->_size = 1;return true;}//插入非根结点pair<Node *,int> ret = Find(key);if(ret.second != -1)//如果没有找到则插入,找到则返回{return false;}else//未找到相同key,进行插入{K newkey = key;Node *cur = ret.first;Node *sub = NULL;while(1){/*1.往节点插入key,sub    2.如果cur没满,则停止,如果满了,则继续向上分裂*/InsertKey(cur,newkey,sub);//已完成节点的插入//判断是否需要分裂if(cur->_size<M)//未满就返回{return true;}size_t mid = cur->_size/2;Node* tmp = new Node;int j=0;    for(int i= mid +1;i<cur->_size;++i){tmp->_key[j] = cur->_key[i];//连续分裂if(cur->_sub[i]){tmp->_sub[j] = cur->_sub[i];tmp->_sub[i+1] = sub;tmp->_sub[i] = NULL;tmp->_sub[j]->_parent = tmp;tmp->_sub[j+1]->_parent = tmp;}tmp->_size++;cur->_key[i] = K();cur->_size--;j++;}if(cur->_parent == NULL)//分裂到根结点{_root = new Node;_root->_key[0] = cur->_key[mid];_root->_sub[0] = cur;_root->_sub[1] = tmp;_root->_size++;cur->_key[mid] = K();cur->_size--;cur->_parent = _root;tmp->_parent = _root;return true;}//没有分裂到根节点    newkey = cur->_key[mid];cur->_key[mid] = K();cur->_size--;sub = tmp;cur = cur->_parent;//上移}}return true;}


中序遍历:

 void Inorder()     {       _Inorder(_root);       cout<<endl;     } 


void _Inorder(Node* root)//打印完一个子树再递归下一步进行打印      {          if(root==NULL)              return;          _Inorder(root->_sub[0]);          for (int i=0;i<root->_size;i++)          {            // _Inorder(root->_subs[i]);             cout<<root->_key[i]<<" ";          }                    for(int i=1;i<M;++i)          {              _Inorder(root->_sub[i]);          }      }  


完整代码:

#include<iostream>#include<vector>using namespace std;template <class K,int M>struct BtreeNode{K _key[M];//关键字数组,多一个便于分裂BtreeNode<K,M> * _parent;BtreeNode<K,M> * _sub[M+1];//指向子树的指针数组int _size;BtreeNode():_size(0),_parent(NULL){    for(int i=0;i<M;i++){_key[i] = K();_sub[i] = NULL;}_sub[M] = NULL;}};//template<class K,class V>//struct Pair//{//K first;//V second;//};template <class K,int M>class Btree{typedef BtreeNode<K,M> Node;public:    Btree():_root(NULL){}pair <Node *,int> Find(const K & key){Node *cur = _root;Node *parent = NULL;while(cur){int i=0;while(i<cur->_size){  if(cur->_key[i]<key)  {++i;  }  else if(cur->_key[i]>key) {break; } else {return pair<Node *, int>(cur,i);//找到返回当前位置 }}parent = cur;cur = cur->_sub[i];}return pair<Node *,int>(parent,-1);//未找到返回-1}bool Insert(const K & key){if(_root == NULL)//根节点为空,创建节点插入key{_root = new Node;_root->_key[0] = key;_root->_size = 1;return true;}//插入非根结点pair<Node *,int> ret = Find(key);if(ret.second != -1)//如果没有找到则插入,找到则返回{return false;}else//未找到相同key,进行插入{K newkey = key;Node *cur = ret.first;Node *sub = NULL;while(1){/*1.往节点插入key,sub    2.如果cur没满,则停止,如果满了,则继续向上分裂*/InsertKey(cur,newkey,sub);//已完成节点的插入//判断是否需要分裂if(cur->_size<M)//未满就返回{return true;}size_t mid = cur->_size/2;Node* tmp = new Node;int j=0;    for(int i= mid +1;i<cur->_size;++i){tmp->_key[j] = cur->_key[i];//连续分裂if(cur->_sub[i]){tmp->_sub[j] = cur->_sub[i];tmp->_sub[i+1] = sub;tmp->_sub[i] = NULL;tmp->_sub[j]->_parent = tmp;tmp->_sub[j+1]->_parent = tmp;}tmp->_size++;cur->_key[i] = K();cur->_size--;j++;}if(cur->_parent == NULL)//分裂到根结点{_root = new Node;_root->_key[0] = cur->_key[mid];_root->_sub[0] = cur;_root->_sub[1] = tmp;_root->_size++;cur->_key[mid] = K();cur->_size--;cur->_parent = _root;tmp->_parent = _root;return true;}//没有分裂到根节点    newkey = cur->_key[mid];cur->_key[mid] = K();cur->_size--;sub = tmp;cur = cur->_parent;//上移}}return true;}    void Inorder()     {       _Inorder(_root);       cout<<endl;     } protected:void InsertKey(Node * node,const K & key,Node * sub){int i = node->_size-1;while(i >= 0){if(node->_key[i] > key){break;}else {//将node[]值循环向后拷贝,空出第一位,同时将相应孩子,也带走node->_key[i+1] = node->_key[i];node->_sub[i+2] = node->_sub[i+1];                --i;} }//循环出来后,指针将指向待插入位置,同时孩子指针也指向相应位置node->_key[i+1] = key;node->_sub[i+2] = sub;if(sub){sub->_parent = node;}node->_size++;}void _Inorder(Node* root)//打印完一个子树再递归下一步进行打印      {          if(root==NULL)              return;          _Inorder(root->_sub[0]);          for (int i=0;i<root->_size;i++)          {            // _Inorder(root->_subs[i]);             cout<<root->_key[i]<<" ";          }                    for(int i=1;i<M;++i)          {              _Inorder(root->_sub[i]);          }      }  private:Node * _root;};void testBTree()  {        Btree<int,3> bt;      int arr[]={53, 75, 139, 49, 145, 36, 101};      for(int i=0;i<sizeof(arr)/sizeof(arr[0]);++i)      {          bt.Insert(arr[i]);      }      bt.Inorder();  }

实现结果:


    

0 0
原创粉丝点击