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
- B,B-,B+,B*树
- B-、B、B+、B*树
- B/B+/B*树
- B- ,B+ , B*树
- B , B+ ,B*树
- B-, B+,B* 树
- B树B-B+树
- B- B+ B*树 小结
- B 、B-、B+树总结
- B-、B+、B*树介绍
- B 树、 B- 树、 B+ 树、 B*
- B树、B-树、B+树、B*
- B树,B-,B+,B*树
- B树、B-、B+、B*树
- B树,B-,B+,B* 各种树
- B,B-,B+ 和B*树
- 辨析B树(B-、B+、B*)
- B树 B+树
- 【译】SOLID:Part 2 - 开闭原则
- POJ1008 日期转换(5)
- 系统调用接口
- Session与Cookie的对比
- jzoj 4890. 【NOIP2016提高A组集训第14场11.12】随机游走 树上期望dp
- B-树
- jQuery过滤选择器——属性过滤选择器
- opencv BUG
- 二进制文件的打开
- 备战国际半程马拉松训练第一天总结
- Qbxt AH d4 && day-6
- 网络请求
- ubuntu14.04+gtx1060配置caffe
- 《MySQL基础语句及命令大全《图文二》》