B-樹:數據庫
一種適合外查找的平衡多叉樹(有些地方寫的是B-樹,注意不要誤讀 成"B減樹") 。數組
M階的B樹知足以下性質:ide
一、根節點至少有兩個孩子;函數
二、每一個非根節點有[[M/2],M]個孩子;spa
三、每一個非根節點有[[M/2],M-1]個關鍵字,而且以升序排列;3d
四、key[i]和key[i+1]之間的孩子節點的值介於key[i]和key[i+1]之間;orm
五、全部的葉子節點都在同一層。blog
M階的B樹----M=3get
插入:it
若是插入值後,該節點的關鍵字的個數大於規定的要求,則須要調整,即分裂(新建立一個節點,把位於關鍵字序列中的中位數(不包含中位數)之後的值包括子樹都拷貝到新建的節點中,將中位數提出成樹的根節點,中位數之前的值將成爲中位數的左子樹,後半部分紅爲右子樹),以下圖:
應用:運用於數據庫和文件系統。
具體實現代碼以下:
#pragma once
//使用外查找的平衡多叉樹
//性質:
// 一、根節點至少有兩個孩子
// 二、每一個非根節點有[[M/2],M]個孩子
// 三、每一個非根節點有[[M/2],M-1]個關鍵字,而且以升序排列
// 四、key[i]和key[i+1]之間的孩子節點的值介於key[i]和key[i+1]之間
// 五、全部的葉子節點都在同一層
template<class K,int M>//B數一個節點的結構
struct BTreeNode
{
K _key[M];//關鍵字的個數爲M-1,多留一個位置能夠更加方便的求取中位數
BTreeNode<K,M>* _subs[M+1];//方便插入時分裂,子樹的最大個數爲M個,關鍵字比他少一個
BTreeNode<K,M>* _parent;//指向父親節點
size_t _size;//數組中存放的有效關鍵字的個數
BTreeNode()
:_parent(NULL)
,_size(0)
{
for(int i = 0;i < M+1;++i)
{
_subs[i] = NULL;
}
}
};
template<class K,class V>//須要返回兩個參數,使用結構體
struct Pair
{
K _first;
V _second;
Pair(const K& key = K(),const V& value = V())//缺省參數,會調用默認構造函數
:_first(key)
,_second(value)
{ }
};
template<class K,int M>
class BTree
{
typedef BTreeNode<K,M> Node;
public:
BTree()
:_root(NULL)
{ }
Pair<Node*,int> Find(const K& key)//查找
{
if(_root == NULL)
return Pair<Node*,int>(NULL,-1);
Node* parent = NULL;
Node* cur = _root;
while (cur)
{
int index = 0;
while (index < cur->_size)
{
if(key == cur->_key[index])
{
return Pair<Node*,int>(cur,index);
}
else if(key < cur->_key[index])
{
break;
}
else
{
index++;
}
}
parent = cur;
cur = cur->_subs[index];
}
return Pair<Node* ,int>(parent,-1);
//找完也沒找到,爲了使得該狀況下方便插入節點,所以返回panrent,插入節點插入在parent上
}
bool Insert(const K& key)//插入
{
//當前無節點
if(_root == NULL)
{
_root = new Node;//開闢一個新的節點
_root->_key[0] = key;
_root->_subs[0] = NULL;
_root->_parent = NULL;
_root->_size++;
return true;
}
Pair<Node*,int> cur = Find(key);
if(cur._second != -1)//找到,則返回false,不插入重複關鍵字
{
return false;
}
//在節點cur中插入key和sub
Node* str = cur._first;
K newKey = key;
Node* sub = NULL;
while (1)
{
_InsertKey(str,newKey,sub);
if(str->_size < M)//關鍵字的數量小於M,則正確,直接返回
return true;
//插入數據後,該節點的關鍵字的個數大於規定的數量,須要調整,進行分裂
int mid = (str->_size - 1)/2;
int index = 0;
Node* temp = new Node;
//先拷貝下標爲mid後的關鍵字
for(size_t i = mid + 1;i < str->_size;i++)
{
temp->_key[index++] = str->_key[i];
temp->_size++;
str->_key[i] = 0;
}
//接着拷貝下標爲mid的關鍵字的sub
index = 0;
for(size_t i = mid + 1;i <= str->_size;i++)
{
temp->_subs[index++] = str->_subs[i];
if(str->_subs[i] != NULL)
str->_subs[i]->_parent = temp;
}
//更改str的大小
str->_size = (str->_size - 1)/2;
if(str->_parent == NULL)
{
_root = new Node;
_root->_key[0] = str->_key[mid];
str->_key[mid] = 0;
_root->_subs[0] = str;
_root->_subs[1] = temp;
_root->_size = 1;
str->_parent = _root;
temp->_parent = _root;
return true;
}
else
{
newKey = str->_key[mid];
str->_key[mid] = 0;
sub = temp;
str = str->_parent;
}
}
}
void InOrder()
{
_InOrder(_root);
cout<<endl;
}
protected:
void _InsertKey(Node* cur,const K& key,Node* sub)
{
int index = cur->_size - 1;
while (index >= 0 && cur->_key[index] > key)//若插入的節點比改位置的值小,則須要移位
{
cur->_key[index+1] = cur->_key[index];
cur->_subs[index+2] = cur->_subs[index+1];
--index;
}
//不然,直接插入
cur->_key[index + 1] = key;
cur->_subs[index+2] = sub;
if(sub != NULL)
sub->_parent = cur;
cur->_size++;
}
void _InOrder(Node* root)
{
if(root == NULL)
return;
for(int i = 0;i < root->_size;i++)
{
_InOrder(root->_subs[i]);
cout<<root->_key[i]<<" ";
}
_InOrder(root->_subs[root->_size]);
}
protected:
Node* _root;
};
void BTreeTest()
{
BTree<int,3> tree;
int a[] = {53,75,139,49,145,36,101};
for(int i = 0;i < sizeof(a)/sizeof(a[i]);i++)
{
tree.Insert(a[i]);
}
tree.InOrder();
}
運行結果: