您的位置:首页 > 其它

平衡搜索树-BTree

2017-11-20 17:33 381 查看
B树是一种适合外查找的树,是一种平衡 的多叉树。

一棵M阶(M>2)的B树,是一棵平衡的M路平衡搜索树,可以是空树或者满足一下性质:

1。根节点至少有两个孩子

2。每个非根节点有[M/2,M]个孩子

3。每个非根节点有[M/2-1,M-1]个关键字,并且以升序排列

4。每个节点孩子的数量比关键字的数量多一个。

5。 key[i]和key[i+1]之间的孩子节点的值介于key[i]、key[i+1]之间

6。 所有的叶子节点都在同一层





下面用代码来实现一下B树

#pragma once

template<class K, class V, size_t M>
struct BTreeNode
{
pair<K, V> _kvs[M];   // 多开一个空间,方便分裂
BTreeNode<K, V, M>* _subs[M+1];
BTreeNode<K, V, M>* _parent;

size_t _size; // 关键字的数量

BTreeNode()
:_parent(NULL)
,_size(0)
{
for (size_t i = 0; i < M+1; ++i)
{
_subs[i] = NULL;
}
}
};

template<class K, class V, size_t M>
class BTree
{
typedef BTreeNode<K, V, M> Node;
public:
BTree()
:_root(NULL)
{}

pair<Node*, int> Find(const K& key)
{
Node* parent = NULL;
Node* cur = _root;
while (cur)
{
size_t i = 0;
while(i < cur->_size)
{
if (cur->_kvs[i].first > key) // 在[i]的左树
break;
else if (cur->_kvs[i].first < key) // 在后面
++i;
else
return make_pair(cur, i);
}

parent = cur;
cur = cur->_subs[i];
}

return make_pair(parent, -1);
}

bool Insert(const pair<K, V>& kv)
{
if (_root == NULL)
{
_root = new Node;
_root->_kvs[0] = kv;
_root->_size = 1;

return true;
}

pair<Node*, int> ret = Find(kv.first);
if (ret.second >= 0)
{
return false;
}

Node* cur = ret.first;
pair<K, V> newKV = kv;
Node* sub = NULL;

// 往cur插入newKV, sub
while (1)
{
InsertKV(cur, newKV, sub);

if (cur->_size < M)
{
return true;
}
else
{
// 分裂
Node* newNode = DivideNode(cur);

pair<K, V> midKV = cur->_kvs[cur->_size/2];
cur->_size -= (newNode->_size+1);

// 1.根节点分裂
if (cur == _root)
{
_root = new Node;
_root->_kvs[0] = midKV;
_root->_size = 1;
_root->_subs[0] = cur;
_root->_subs[1] = newNode;
cur->_parent = _root;
newNode->_parent = _root;
return true;
}
else
{
sub = newNode;
newKV = midKV;
cur = cur->_parent;
}
}
}
}

Node* DivideNode(Node* cur)
{
Node* newNode = new Node;
int mid = cur->_size/2;

size_t j = 0;
size_t i = mid+1;
for (; i < cur->_size; ++i)
{
newNode->_kvs[j] = cur->_kvs[i];
newNode->_subs[j] = cur->_subs[i];
if(newNode->_subs[j])
newNode->_subs[j]->_parent = newNode;
newNode->_size++;
j++;
}

newNode->_subs[j] = cur->_subs[i];
if(newNode->_subs[j])
newNode->_subs[j]->_parent = newNode;

return newNode;
}

void InsertKV(Node* cur, const pair<K, V>& kv, Node* sub)
{
int end = cur->_size-1;
while (end >= 0)
{
if (cur->_kvs[end].first > kv.first)
{
cur->_kvs[end+1] = cur->_kvs[end];
cur->_subs[end+2] = cur->_subs[end+1];
--end;
}
else
{
break;
}
}

cur->_kvs[end+1] = kv;
cur->_subs[end+2] = sub;
if(sub)
sub->_parent = cur;

cur->_size++;
}

void InOrder()
{
_InOrder(_root);
cout<<endl;
}

void _InOrder(Node* root)
{
if (root == NULL)
return;

Node* cur = root;
size_t i = 0;
for (; i < cur->_size; ++i)
{
_InOrder(cur->_subs[i]);
cout<<cur->_kvs[i].first<
4000
<" ";
}

_InOrder(cur->_subs[i]);
}

private:
Node* _root;
};

void TestBTree()
{
BTree<int, int, 3> t;
int a[] = {53, 75, 139, 49, 145, 36, 101};
for (size_t i = 0; i < sizeof(a)/sizeof(a[0]); ++i)
{
t.Insert(make_pair(a[i], i));
}

t.InOrder();
}

template<class K, size_t M>
struct BPTreeNonLeafNode
{
K _keys[M];
void* _subs[M];
BPTreeNonLeafNode<K, M>* _parent;
size_t _size;
};

template<class K, class V, size_t M>
struct BPTreeLeafNode
{
pair<K, V> kvs[M];
BPTreeLeafNode<K, V>* _next;
BPTreeNonLeafNode<K, M>* _parent;
size_t _size;
};

template<class K, class V, size_t M>
class BPTree
{
typedef BPTreeLeafNode<K, V, M> LeafNode;
typedef BPTreeLeafNode<K, M> NonLeafNode;
private:
NonLeafNode* _root;
LeafNode* _head;
};


B+树

B+树是对B树的一种变形树,它与B树的差异在于:

有k个子结点的结点必然有k个关键码;

非叶结点仅具有索引作用,跟记录有关的信息均存放在叶结点中。

树的所有叶结点构成一个有序链表,可以按照关键码排序的次序遍历全部记录。



B+树更适合文件索引系统

B*树



B*树定义了非叶子结点关键字个数至少为(2/3)*M,即块的最低使用率为2/3

(代替B+树的1/2);

B+树的分裂:当一个结点满时,分配一个新的结点,并将原结点中1/2的数据

复制到新结点,最后在父结点中增加新结点的指针;B+树的分裂只影响原结点和父

结点,而不会影响兄弟结点,所以它不需要指向兄弟的指针;

B*树的分裂:当一个结点满时,如果它的下一个兄弟结点未满,那么将一部分

数据移到兄弟结点中,再在原结点插入关键字,最后修改父结点中兄弟结点的关键字

(因为兄弟结点的关键字范围改变了);如果兄弟也满了,则在原结点与兄弟结点之

间增加新结点,并各复制1/3的数据到新结点,最后在父结点增加新结点的指针;

所以,B*树分配新结点的概率比B+树要低,空间使用率更高;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: