1.B树的概念
一棵M阶的(M>2) 的平衡二叉树,是一棵平衡的M路平衡搜索树,可以是空树或者满足一下性质:
根节点至少有两个孩子;
原因:因为根节点至少有一个关键字,有两个指针域;每个非根节点至少有M/2-1(上取整)个关键字,至多有M-1个关键字,并且以升序排列;
- 每个非根节点至少有M/2(上取整)个孩子,至多有M个孩子;
- key[i]和key[i+1]之间的孩子节点的值介于key[i]、key[i+1]之间
- 所有的叶子节点都在一层(由节点分裂可推导出);
2.为什么我们要引入B树
数据一般保存在磁盘上,若数据量过大则不能全部加载到内存,为了访问所有的数据,我们可以采用二叉搜索树的索引来保存数据,树的节点保存权值和磁盘的地址,这样查找时可以节约时间;
但是这并没有改善上述的问题,当数据量过大,树的高度则会增高,时间复杂度O(logN)还是太大,为了降低树的高度,减少时间复杂度,我们采用多叉搜索树;
3.B树的构建
首先我们应该清楚B树节点中包含了什么内容,如下所示
节点的分裂原则,如下例所示
再插入节点38和35
4.代码实现
#include<stdio.h>
#include<utility>
#include<iostream>
using namespace std;
template<class k,size_t M>
struct BTreeNode
{
BTreeNode()
:_size(0)
,_pParent(NULL)
{
for(size_t idx=0; idx<M+1; ++idx)
_pSub[idx] = NULL;
}
k _keys[M]; //存储键值的数组
BTreeNode* _pSub[M+1]; //存储孩子指针域的指针
BTreeNode* _pParent;
size_t _size; //有效键值的个数
};
template<class k,size_t M>
class BTree
{
typedef BTreeNode<k,M> Node;
public:
BTree()
:_pRoot(NULL)
{}
bool Insert(const k& key)
{
if(_pRoot == NULL)
{
_pRoot = new Node;
_pRoot->_keys[0] = key;
_pRoot->_size = 1;
return true;
}
//查找插入节点的位置
pair<Node*,int> ret = Find(key);
if(ret.second > -1)
return false;
Node* pNode = ret.first;
Node* pSub = NULL;
k valuek = key;
//将key值插入我们已经找到的节点
while(true)
{
_Insert(pNode,valuek,pSub);
if(pNode->_size < M)
return true;
//需要对节点进行分裂
Node* pNewNode = new Node;
size_t mid = M/2;
size_t index = 0;
size_t idx = 0;
//搬移元素和孩子指针到pNewNode
for(idx=mid+1; idx<pNode->_size; ++idx)
{
pNewNode->_keys[index] = pNode->_keys[idx];
pNewNode->_pSub[index] = pNode->_pSub[idx];
if(pNode->_pSub[idx])
{
pNode->_pSub[idx]->_pParent = pNewNode->_pSub[index];
pNode->_pSub[idx] = NULL;
}
pNewNode->_size++;
pNode->_size--;
index++;
}
pNewNode->_pSub[index] = pNode->_pSub[idx];
if(pNode->_pSub[idx])
{
pNode->_pSub[idx]->_pParent = pNewNode->_pSub[index];
pNode->_pSub[idx] = NULL;
}
pNode->_size = pNode->_size - pNewNode->_size;
//判断该节点是否为根节点
if(pNode->_pParent == NULL)
{
_pRoot = new Node;
_pRoot->_keys[0] = pNode->_keys[mid];
_pRoot->_pSub[0] = pNode;
pNode->_pParent = _pRoot;
_pRoot->_pSub[1] = pNewNode;
pNewNode->_pParent = _pRoot;
_pRoot->_size++;
return true;
}
else
{
valuek = pNode->_keys[mid];
pNode = pNode->_pParent;
pSub = pNewNode;
}
}
}
pair<Node*,int> Find(const k& key)
{
Node* pCur = _pRoot;
Node* pParent = NULL;
while(pCur)
{
int index = 0;
while(index < pCur->_size)
{
if(pCur->_keys[index] > key)
{
//pCur = pCur->_pSub[index];
break;
}
else if(pCur->_keys[index] < key)
index++;
else
return pair<Node*,int>(pCur,index);
}
//if(key > pCur->_keys[index-1])
pParent = pCur;
pCur = pCur->_pSub[index];//当key比数组最后一个key值还大时
}
return pair<Node*,int>(pParent,-1);
}
private:
void _Insert(Node*& pNode,const k& key,Node* pSub)
{
int end = pNode->_size-1;
while(end > -1)
{
if(pNode->_keys[end] > key)
{
pNode->_keys[end+1] = pNode->_keys[end];
pNode->_pSub[end+2] = pNode->_pSub[end+1];
}
else
break;
end--;
}
pNode->_keys[end+1] = key;
pNode->_pSub[end+2] = pSub;
if(pSub)
pSub->_pParent = pNode;
pNode->_size++;
}
private:
Node* _pRoot;
};
测试代码:
#include"BTree.cpp"
void funtest()
{
BTree<int,3> b;
b.Insert(10);
b.Insert(30);
b.Insert(20);
b.Insert(40);
b.Insert(50);
b.Insert(38);
b.Insert(35);
}
int main()
{
funtest();
getchar();
return 0;
}