关于B-树基础知识可以参看:
http://student.zjzk.cn/course_ware/data_structure/web/chazhao/chazhao9.3.2.1.htm
B-树的数据结构如下:
class TreeNode { private: int keynum; TreeNode* parent; TreeNode* ptr[m+1];//0...m use to store pointer int key[m+1];//1...m use to store k public: TreeNode() { keynum = 0; } TreeNode(TreeNode* p1, int k, TreeNode *p2)//root node { keynum = 1; key[1] = k; parent = 0; fill(ptr, ptr+m+1, (TreeNode*)0); ptr[0] = p1; ptr[1] = p2; if(p1) p1->parent = this; if(p2) p2->parent = this; } TreeNode(TreeNode* p, int *keyarry, TreeNode **ptrarry) { parent = p; fill(ptr, ptr+m+1, (TreeNode*)0); int temp = ceil(m/2.0);//rounding up ptr[0] = ptrarry[temp]; if(ptr[0]) ptr[0]->parent = this; for(int i = 1; i+temp <= m; ++i) { key[i] = keyarry[i+temp]; ptr[i] = ptrarry[i+temp]; if(ptr[i]) ptr[i]->parent = this; keyarry[i+temp] = 0; ptrarry[i+temp] = 0; } keyarry[temp] = 0; ptrarry[temp] = 0; keynum = m-temp; } friend class Result; friend class Btree; }; class Result { public: TreeNode* Resultptr; int index; bool Resultflag; Result(TreeNode* p = 0, int i = 0, bool r=0) :Resultptr(p), index(i), Resultflag(r) {} friend class Btree; }; class Btree { public: Btree() { root = 0; } void InsertBtree(const int k); Result Find(const int k)const; void DeleteBtree(const int k); void Insert(const int k, TreeNode* node, TreeNode* a); void Display()const; void Search(const int k)const; private: TreeNode* root; /*for delete*/ void BorrowOrCombine(TreeNode* a, const int i, const int type, stack<int>& s); };
其中TreeNode是B-树的结点,TreeNode在private里包含了结点所有的信息,同时包含了三个构造函数。其中第一个不带参数的构造函数,只是为了初始化空节点;第二个构造函数为了创建B-树的根节点;第三个构造函数,包含三个参数,目的是为了在插入的时候分裂a结点,其中p是a结点的parent指针,keyarry、ptrarry分别为结点a的key数组以及结点a的ptr指针数组,a分裂后产生的新的结点的父节点为p。
Result类主要用于封装查找关键字的返回结果,其中Resultptr查找结果的B-树结点,如果包含key则返回该结点,如果不包含key则返回待插入值key的叶子节点,index为key在该节点中的位置或key将要插入的结点的位置,Resultflag表示是否包含key值。
Btree类主要用来封装root结点和与B-树相关的函数。
B-树的成员函数实现结果如下:
#include"btree.h" #include<iostream> #include<cmath> #include<stack> #include<queue> using namespace std; void Btree::InsertBtree(const int k) { if(!root) { root = new TreeNode(0,k,0); return; } Result res = Find(k); TreeNode* a = res.Resultptr; if(res.Resultflag == true) return; Insert(k, 0, a); } void Btree::Search(const int k)const { Result res = Find(k); if(res.Resultflag == true) cout<<"find it"<<endl; else cout<<"not find"<<endl; } void Btree::Insert(const int k, TreeNode* node, TreeNode* a) { int i = 1;//index to insert while(i <= a->keynum) { if(k <= a->key[i]) { break; } i++; } for(int j = a->keynum; j >= i; --j) { a->key[j+1] = a->key[j]; a->ptr[j+1] = a->ptr[j]; } a->key[i] = k; a->ptr[i] = node; if(node) node->parent = a; ++a->keynum; if(a->keynum <= m-1 ) { return; } else { /*slipt*/ int midkey = a->key[(int)ceil(m/2.0)]; TreeNode* newnode = new TreeNode(a->parent, a->key, a->ptr); a->keynum = m - ceil(m/2.0); TreeNode* tempa = a;//restore a a = a->parent; if(!a) { TreeNode* newRoot = new TreeNode(tempa,midkey,newnode); tempa->parent = newRoot; newnode->parent = newRoot; root = newRoot; return; } else { Insert(midkey,newnode,a); } } } Result Btree::Find(const int k)const { if(!root) { cout<<"the tree is empty"<<endl; return Result(0,0,0); } TreeNode* a = root; int i = 1; while(a) { i = 1; while(i <= a->keynum) { if(a->key[i] >= k) { break; } else ++i; } if(k == a->key[i]) { return Result(a, i, 1); } else { if(a->ptr[i-1]) { a = a->ptr[i-1]; } else { return Result(a, i, 0); } } } } void Btree::DeleteBtree(const int k) { if(!root) { cout<<"The tree is null!"<<endl; return; } stack<int> s;//store the all index TreeNode* delnode = root; int i = 1; while(delnode) { i = 1; while(i <= delnode->keynum ) { if(k <= delnode->key[i]) { break; } else { i++; } } if(k == delnode->key[i]) { cout<<"find it"<<endl; break; } else { if(delnode->ptr[i-1] == 0) { cout<<"no this key"<<endl; return; } else { delnode = delnode->ptr[i-1]; s.push(i-1); } } } TreeNode* p = delnode; //store delnode if(delnode->ptr[i]) { s.push(i); p = delnode->ptr[i]; while(p->ptr[0]) { p = p->ptr[0]; if(!p->ptr[0]) break; s.push(0); } } if(p != delnode) { delnode->key[i] = p->key[1]; i = 1; } BorrowOrCombine(p, i, 0, s); } void Btree::BorrowOrCombine(TreeNode* a,const int i,const int type, stack<int>& s) { if(a == root && root->keynum == 1) { TreeNode* oldroot = root; if(type == -1) { if(root->ptr[i-1]) root = root->ptr[i-1]; else root = 0; } else if(type == 1) { if(root->ptr[i]) { root = root->ptr[i]; } else root = 0; } else { root = 0; } if(root) root->parent = 0; delete oldroot; return; } int minnum = ceil(m/2.0)-1; TreeNode *la,*ra; int j; if(a->keynum > minnum|| a == root) { TreeNode* tempstr; if(type == -1) { tempstr = a->ptr[i-1]; } else { tempstr = a->ptr[i]; } for(j = i; j < a->keynum; ++j) { a->key[j] = a->key[j+1]; a->ptr[j] = a->ptr[j+1]; } a->ptr[i-1] = tempstr; a->key[j] = 0; a->ptr[j] = 0; --a->keynum; } else { int index = s.top(); s.pop(); if(index)//have left brother { la = a->parent->ptr[index - 1];//left brother if(la->keynum > minnum)//left brother has enough elements { TreeNode* tempstr; if(type == -1) { tempstr = a->ptr[i-1]; } else { tempstr = a->ptr[i]; } for(j = i; j > 1; --j) { a->key[j] = a->key[j-1]; a->ptr[j] = a->ptr[j-1]; } a->ptr[i] = tempstr; a->key[1] = a->parent->key[index]; a->ptr[0] = la->ptr[la->keynum]; if(la->ptr[la->keynum]) la->ptr[la->keynum]->parent = a; a->parent->key[index] = la->key[la->keynum]; la->ptr[la->keynum] = 0; la->key[la->keynum] = 0; --la->keynum; return; } } else if(index < a->parent->keynum)//have right brother { ra = a->parent->ptr[index+1]; if(ra->keynum > minnum)// have lots of elements { TreeNode* tempstr; if(type == -1) { tempstr = a->ptr[i-1]; } else { tempstr = a->ptr[i]; } for(j = i; j < a->keynum; j++) { a->key[j] = a->key[j+1]; a->ptr[j] = a->ptr[j+1]; } a->ptr[i-1] = tempstr; a->key[a->keynum] = a->parent->key[index+1]; a->ptr[a->keynum] = ra->ptr[0]; if(ra->ptr[0]) ra->ptr[0]->parent = a; a->parent->key[index+1] = ra->key[1]; ra->ptr[0] = ra->ptr[1]; for(j = 1; j < ra->keynum; j++) { ra->key[j] = ra->key[j+1]; ra->ptr[j] = ra->ptr[j+1]; } ra->key[ra->keynum] = 0; ra->ptr[ra->keynum] = 0; --ra->keynum; return; } } if(index)//have left brother donnot hava enough elements { cout<<la->key[1]; la = a->parent->ptr[index-1]; cout<<a->parent->key[index]<<a->key[i]<<endl; if(la->keynum == minnum)//donnot have enough elements { /*combine left brother*/ la->key[la->keynum+1] = a->parent->key[index]; for(j =1; j <= i-1; ++j) { la->ptr[la->keynum+j] = a->ptr[j-1]; la->key[la->keynum+j+1] = a->key[j]; if(a->ptr[j-1]) a->ptr[j-1]->parent = la; } if(type == -1) { la->ptr[la->keynum+i] = a->ptr[i-1]; if(a->ptr[i-1]) a->ptr[i-1]->parent = la; } else { la->ptr[la->keynum+i] = a->ptr[i]; if(a->ptr[i]) { a->ptr[i]->parent = la; } } for(j = i+1; j <= a->keynum; ++j) { la->key[la->keynum+j] = a->key[j]; la->ptr[la->keynum+j] = a->ptr[j]; if(a->ptr[j]) a->ptr[j]->parent = la; } la->keynum = m-1; TreeNode* tempp = a->parent; tempp->ptr[index] = 0; delete a; BorrowOrCombine(tempp,index,-1,s); return; } } if(index < a->keynum)//have right brother { ra = a->parent->ptr[index+1]; cout<<"have right brother"<<endl; if( ra->keynum == minnum)// right brother do not have enough elements { int step = m - 1 - ra->keynum; for(j = ra->keynum; j >0; --j) { ra->key[j + step] = ra->key[j]; ra->ptr[j + step] = ra->ptr[j]; } ra->key[step] = a->parent->key[index+1]; ra->ptr[step] = ra->ptr[j]; --step; for(j = a->keynum; j > i; --j,--step) { ra->ptr[step] = a->ptr[j]; if(a->ptr[j]) { a->ptr[j]->parent = ra; } ra->key[step] = a->key[j]; } if(type == -1) { ra->ptr[step] = a->ptr[j - 1]; if(a->ptr[j - 1]) { a->ptr[j - 1]->parent = ra; } } else { ra->ptr[step] = a->ptr[j]; if(a->ptr[j]) { a->ptr[j]->parent = ra; } } for(int x = i-1, j = i-1; x > 0; --x, --j) { ra->key[x] = a->key[j]; ra->ptr[x-1] = a->ptr[j-1]; if(ra->ptr[x-1]) ra->ptr[x-1]->parent = ra; } ra->keynum = m-1; TreeNode* ap = a->parent; ap->ptr[index] = 0; delete a; BorrowOrCombine(ap, index+1, 1, s); } } } } void Btree::Display()const { if(!root) { cout<<"Btree is empty!"<<endl; } else { TreeNode* p = root; queue<TreeNode*> qu; while(p) { qu.push(p->ptr[0]); for(int j = 1; j<= p->keynum; j++) { if(p->ptr[j]) qu.push(p->ptr[j]); cout<<p->key[j]<<"-"; } cout<<"his parent"<<endl; TreeNode* q = p->parent; if(q) { for(int j = 1; j<= q->keynum; j++) { //qu.push(q->ptr[j]); cout<<q->key[j]<<"-"; } } cout<<" "; p = qu.front(); qu.pop(); cout<<endl; } } }
其中,插入和删除操作是整个实现代码的核心。
插入操作的思想是,先插入后分裂。即不管待插入结点的元素数量,先将元素插入到指定位置,然后再判断元素数量是否超过B-树的最大值,如果超越,通过TreeNode的第三个构造函数对结点进行分裂,分裂后将结点的中心值midkey插入到父节点的相应位置,再次判断,直到满足条件结束。代码通过不断递归实现。
删除操作是B-树的难点和核心。删除一个key值首先判断key值在不在叶子节点,如果不在通过下面的方法将问题转化为删除叶子节点的一个key值。
非叶子节点key值删除转换方法,首先将key中序后继(前驱,本代码选择后续)的key值代替待删除的key,也就是说使用当前节点key值的右子树的最左节点的第一个key值替换待删除的key,这样将问题转化为删除叶子节点的key值。
删除叶子节点key值分为四种情况:
1、当前叶子节点的keynum>minnum时,只需将待删除key值后面的可以key值以及ptr值顺序向前覆盖,即可完成。
2、当前叶子节点的keynum == minum,叶子节点的左兄弟keynum > minnum,此时将叶子节点key值前面的key序列和ptr序列顺序后移一位,腾出第一个key值的位置和ptr[0]的位置,然后将对应的父节点的值移入到key[1],左兄弟的最后一个节点对应的指针赋值给当前节点ptr[0],左兄弟最后一个key值赋值给对应的父节点的key。即可完成。
3、当前叶子节点的keynum== minum,叶子节点的右兄弟keynum >minnum,参照情况二。
4、当前叶子节点的keynum == minum,叶子节点左兄弟为keynum ==minnum,这时候就需要对结点进行合并,将左节点、父节点以及当前节点合并,合并的时候需要注意,只将父节点对应的值置为0,不改变父节点的结构,同时传递一个有效指针的位置参数。即当前节点与左兄弟合并时,传递-1,表示父节点值对应的左边子树指针为有效节点,同理与右兄弟合并时传递1。继续以与左兄弟合并为例,左兄弟的内容不变,在后面加入父节点对应的值以及当前节点剩余的值以及指针,指针的顺序不改变。合并完成后,对父节点进行调整,我们知道父节点已经少了一个值,同时接收了标记参数type,接下来从1开始再次进行判断,知道递归完成。
5、当前叶子节点的keynum == minum,叶子节点右兄弟为keynum ==minnum,参照情况4将当前节点、父节点对应的key值以及右兄弟合并。同时一步步递归直到完成。
算法的一个比较重要的设计想法是,在进行合并操作时只进行本层节点的合并,对于父节点留作下一此迭代来完成,同时将有效指针的参数type传递给上层节点。