B-樹的插入和遍歷
阿新 • • 發佈:2018-12-23
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]是向上取整
先看看B-樹節點的結構:
template<class K,size_t M = 3> struct BTreeNode { K _key[M]; //關鍵字 BTreeNode<K, M>* _subs[M + 1]; //孩子 BTreeNode<K, M>* _parent; size_t _size; //實際關鍵字個數 BTreeNode() :_parent(NULL) , _size(0) { memset(_subs, 0, sizeof(_subs)); } };
插入的幾種情況:
1.沒有節點,即root == NULL;
這個是最簡單,直接建立一個節點,將key值放入;
if (_root == NULL)
{
_root = new Node;
_root->_key[0] = key;
_root->_size++;
}
2.有節點,找到要插入的位置,進行插入。完畢之後必須進行是否需要進行分裂的判斷。分裂保證第2,3條性質。分裂完後繼續判斷父親是否需要分裂,直到找到不需要分裂的位置結束。
3.每次插入一個key都會帶一個子節點(NULL)。以保證孩子比key多一個的性質;向上調整時,也可以看作是向父親接待你插入一個新的key,所帶子節點為分裂出來的節點(newNode)。
插入程式碼實現:
上述就說完了插入,下面來看看中序遍歷:bool Insert(const K &key) { //沒有節點 if (_root == NULL) { _root = new Node; _root->_key[0] = key; _root->_size++; } //找到key的節點 pair<Node*,int> ret = Find(key); if (ret.second >= 0) { return false; //已存在,不需要插入 } //進行插入 K newKey = key; Node *cur = ret.first; Node *sub = NULL; while (1) { InsertKey(newKey, cur, sub); //在cur節點插入一個newKey ,所帶的孩子為sub //判斷分裂 if (cur->_size < M) { return true; } //開始分裂 Node *newNode = new Node; size_t mid = M / 2; int index = 0; //構造分裂出來的newNode size_t i = mid + 1; for (; i < cur->_size; i++) { //提取mid後面的資料到newNode中:key,左邊孩子 newNode->_key[index] = cur->_key[i]; newNode->_size++; newNode->_subs[index] = cur->_subs[i]; ++index; if (cur->_subs[i]) { Node *sb = cur->_subs[i]; cur->_subs[i] = NULL; sb->_parent = newNode; } } newNode->_subs[index] = cur->_subs[i]; cur->_size = cur->_size - newNode->_size - 1; if (cur->_subs[i]) { Node *sb = cur->_subs[i]; cur->_subs[i] = NULL; sb->_parent = newNode; } if (cur->_parent == NULL) //分裂的節點是第一個節點 { _root = new Node; _root->_key[0] = cur->_key[mid]; _root->_subs[0] = cur; cur->_parent = _root; _root->_subs[1] = newNode; newNode->_parent = _root; _root->_size++; return true; } else //分裂的節點有父親節點,需要判斷父親節點是否分裂 { newKey = cur->_key[mid]; cur = cur->_parent; sub = newNode; } } }
遍歷就十分簡單,直接看程式碼
void _Inorder(Node *root)
{
if (root == NULL)
{
return;
}
int i = 0;
for (; i < root->_size; i++) //遍歷所有的key才是這個節點遍歷完
{
_Inorder(root->_subs[i]);
cout << root->_key[i] << " ";
}
_Inorder(root->_subs[i]);
}
總體實現程式碼:
//B-樹
template<class K,size_t M = 3>
struct BTreeNode
{
K _key[M]; //關鍵字
BTreeNode<K, M>* _subs[M + 1]; //孩子
BTreeNode<K, M>* _parent;
size_t _size; //實際關鍵字個數
BTreeNode()
:_parent(NULL)
, _size(0)
{
memset(_subs, 0, sizeof(_subs));
}
};
template<class K,size_t M = 3>
class BTree
{
typedef BTreeNode<K, M> Node;
public:
BTree()
:_root(NULL)
{}
bool Insert(const K &key)
{
//沒有節點
if (_root == NULL)
{
_root = new Node;
_root->_key[0] = key;
_root->_size++;
}
//找到key的節點
pair<Node*,int> ret = Find(key);
if (ret.second >= 0)
{
return false; //已存在,不需要插入
}
//進行插入
K newKey = key;
Node *cur = ret.first;
Node *sub = NULL;
while (1)
{
InsertKey(newKey, cur, sub);
//判斷分裂
if (cur->_size < M)
{
return true;
}
//開始分裂
Node *newNode = new Node;
size_t mid = M / 2;
int index = 0;
//構造分裂出來的newNode
size_t i = mid + 1;
for (; i < cur->_size; i++)
{
//提取mid後面的資料到newNode中:key,左邊孩子
newNode->_key[index] = cur->_key[i];
newNode->_size++;
newNode->_subs[index] = cur->_subs[i];
++index;
if (cur->_subs[i])
{
Node *sb = cur->_subs[i];
cur->_subs[i] = NULL;
sb->_parent = newNode;
}
}
newNode->_subs[index] = cur->_subs[i];
cur->_size = cur->_size - newNode->_size - 1;
if (cur->_subs[i])
{
Node *sb = cur->_subs[i];
cur->_subs[i] = NULL;
sb->_parent = newNode;
}
if (cur->_parent == NULL)
{
_root = new Node;
_root->_key[0] = cur->_key[mid];
_root->_subs[0] = cur;
cur->_parent = _root;
_root->_subs[1] = newNode;
newNode->_parent = _root;
_root->_size++;
return true;
}
else
{
newKey = cur->_key[mid];
cur = cur->_parent;
sub = newNode;
}
}
}
void InsertKey(const K &key, Node *cur, Node *sub) //cur要被差的節點,sub為key所帶的孩子
{
assert(cur);
int end = cur->_size - 1;
while (end >= 0)
{
if (cur->_key[end] < key)
{
break;
}
else //後移
{
cur->_key[end + 1] = cur->_key[end];
cur->_subs[end + 2] = cur->_subs[end + 1];
}
end--;
}
//找到插入位置;
cur->_key[end + 1] = key;
cur->_subs[end + 2] = sub;
cur->_size++;
if (sub)
sub->_parent = cur;
}
pair<Node*, int> Find(const K &key)
{
Node *cur = _root;
Node *parent = NULL;
while (cur)
{
size_t i = 0;
for (; i < cur->_size;)
{
if (cur->_key[i] < key)
{
++i;
}
else if (cur->_key[i] > key)
{
break;
}
else //key已經出現在樹中
{
return make_pair(cur, 0);
}
}
parent = cur;
cur = cur->_subs[i];
}
return make_pair(parent, -1); //沒有找到key
}
void Inorder()
{
_Inorder(_root);
}
protected:
void _Inorder(Node *root)
{
if (root == NULL)
{
return;
}
int i = 0;
for (; i < root->_size; i++)
{
_Inorder(root->_subs[i]);
cout << root->_key[i] << " ";
}
_Inorder(root->_subs[i]);
}
protected:
Node *_root;
};