B树也是一种搜索树,二叉搜索树、红黑树、都是动态查找树,典型的二叉搜索结构,查找的时间复杂度和树的高度相关O(log2N)。
这些二叉搜索结构有一个共同的缺陷:数据量大,树的高度太高,增大访问磁盘的次数,从而效率低下。
想要加速对数据的访问速度:
1.提高I/O的时间
2.降低树的高度——平衡多叉树
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.所有的叶子节点都在同一层。
例:
插入{53,75,139,49,145,36,101};
M阶B树——M=3
3阶B树的插入过程:
代码实现过程:
1.首先设定结构体,实际只用到两个关键码,但是为了交换简单,我们设置三个关键码,孩子指针域始终比关键码多一个。
2.创建一个类将B树的函数操作封装起来。
3.B树的插入:
(1)根节点为空,开辟一个新节点newNode,将key的值赋给newNode,对size+1,最后使根指向newNode。
(2)根不为空,通过Find函数找到插入位置cur,通过_InsertKey()函数将key的值赋给cur,(cur->size)+1,判断cur->size与M的大小,如果cur->size < M则return true;
(3)如果cur->size=M,要将节点进行分裂,将中间的关键码向上提,new一个节点将最右边的关键码赋值给key[0]。
(4)每次改变节点要记得更改双亲的指向。
【BTree.h】
#pragma once
#include<iostream>
using namespace std;
#define M 3
template<class K>
struct BTreeNode
{
K _keys[M]; //关键码
BTreeNode<K>* _pSub[M + 1]; //孩子指针域
BTreeNode<K>* _pParent;
size_t size;
BTreeNode()
:_pParent(NULL)
, size(0)
{
for (int i = 0; i < (M + 1); i++)
_pSub[i] = NULL;
}
};
template<class K>
class BTree
{
typedef BTreeNode<K> Node;
typedef Node* pNode;
public:
BTree()
:pRoot(NULL)
{}
bool BTree_Insert(K value)
{
if (pRoot == NULL)
{
pRoot = new Node;
pRoot->_keys[0] = value;
pRoot->size = 1;
return true;
}
//节点的关键字满了就进行分裂
pair<pNode,int> findNode = _Find(value);
if (findNode.second >= 0) //找到相同关键码
return false;
//没找到相同节点,可以插入新节点
pNode cur = findNode.first;
K newkey = value;
pNode sub = NULL;
//向cur插入newkey,sub
while (1)
{
_InsertKey(cur, newkey, sub); //插入一个孩子和一个关键字
if (cur->size < M)
return true;
else
{
//需要分裂
pNode newNode = _splitblock(cur);
K midkey = cur->_keys[(cur->size) / 2];
//根节点分裂
cur->size = (cur->size) - (newNode->size + 1);
if (cur == pRoot)
{
pRoot = new Node;
pRoot->_keys[0] = midkey;
pRoot->size = 1;
pRoot->_pSub[0] = cur;
pRoot->_pSub[1] = newNode;
cur->_pParent = pRoot;
newNode->_pParent = pRoot;
return true;
}
else
{
sub = newNode;
newkey = midkey;
cur = cur->_pParent;
}
}
}
}
void InOrder() //中序遍历
{
_InOrder(pRoot);
cout << endl;
}
private:
pNode _splitblock(pNode cur) //分裂函数
{
pNode newNode = new Node;
int mid = (cur->size) / 2;
size_t j = 0;
size_t i = mid + 1;
for (; i < cur->size; i++)
{
newNode->_keys[j] = cur->_keys[i];
newNode->_pSub[j] = cur->_pSub[i];
if (newNode->_pSub[j] != NULL)
newNode->_pSub[j]->_pParent = newNode;
newNode->size++;
j++;
}
//拷右孩子
newNode->_pSub[j] = cur->_pSub[i];
if (cur->_pSub[i] != NULL)
newNode->_pSub[j]->_pParent = newNode;
return newNode;
}
void _InsertKey(pNode cur,const K value,pNode sub) //插入一个孩子和一个关键码
{
int end = cur->size - 1;
while (end >= 0)
{
if (cur->_keys[end] > value)
{
cur->_keys[end + 1] = cur->_keys[end];
cur->_pSub[end + 2] = cur->_pSub[end + 1];
end--;
}
else
break;
}
//当end<0 或 value > cur->keys[end]
cur->_keys[end + 1] = value;
cur->_pSub[end + 2] = sub;
if (sub != NULL)
sub->_pParent = cur;
cur->size++;
}
pair<pNode,int> _Find(K value)
{
pNode ret = pRoot;
pNode parent = NULL;
while (ret)
{
size_t i = 0;
while (i < ret->size)
{
//在当前位置的左树
if (value < (ret->_keys[i]))
break;
else if (value>(ret->_keys[i]))
{
i++;
}
else //有相同关键码
return make_pair(ret, i);
}
//在左树或没找到
parent = ret;
ret = ret->_pSub[i];
}
return make_pair(parent,-1);
}
void _InOrder(pNode _pRoot) //中序遍历
{
if (_pRoot == NULL)
return;
pNode cur = _pRoot;
size_t i = 0;
for (i = 0; i < cur->size; i++)
{
_InOrder(cur->_pSub[i]);
cout << cur->_keys[i] << " ";
}
_InOrder(cur->_pSub[i]);
}
private:
pNode pRoot;
};
void test()
{
int arr[] = { 53, 75, 139, 49, 145, 36, 101 };
int size = sizeof(arr) / sizeof(arr[0]);
BTree<int> bt;
for (int i = 0; i < size; i++)
{
bt.BTree_Insert(arr[i]);
}
bt.InOrder();
}
【test.cpp】
#include"BTree.h"
int main()
{
test();
system("pause");
return 0;
}