红黑树 , B+ 树 ¶
红黑树 ¶
红黑树的定义满足以下性质:
- 每个结点要么是红的,要么是黑的。
- 根结点是黑的。
- 每个叶结点(NIL 结点,空结点)是黑的。
- 如果一个结点是红的,那么它的两个儿子都是黑的。
- 对每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点。
复杂度证明 ¶
Definition
- NIL 称为外部结点,其余有键值的结点称为内部结点。
- 从某个结点 \(X\) 出发到达一个叶子结点(NIL)的任意一条简单路径上的黑色结点个数(不含 \(X\) 本身)称为 \(X\) 的黑高,记为 \(\operatorname{bh}(X)\)。整棵红黑树的黑高是其根结点的黑高。
Theorem
一棵有 \(n\) 个内部结点的红黑树的高度至多为 \(2 \ln (n + 1)\)。
Proof
对于任意结点 \(x\),\(\operatorname{sizeof}(x) \geqslant 2^{\operatorname{bh}(x)} - 1\). \(\operatorname{sizeof}(x)\) 表示以 \(x\) 为根的子树的内部结点个数。
\(\operatorname{bh}(T) \geqslant h(T) / 2\),其中 \(h(T)\) 表示树 \(T\) 的高度。
对于红黑树,黑高是绝对严格的平衡要求,而红色结点则是少量不平衡的因素,并且定义控制了红色结点的个数,也就控制了不平衡因素的影响,因此红黑树还是可以保持一定程度的平衡的。其插入,删除操作的时间复杂度都是 \(O(\log n)\)。
插入 ¶
将情况递归到父亲的父亲。
删除 ¶
与 BST 的删除类似,寻找前驱或后继结点替换。
- 递归到最后就是删除“叶子结点”。
- 需要维持红黑树的性质。
- 红色:直接删除。
- 黑色:需要在到达此结点的简单路径上添加一个黑色结点,并且不改变其余结点的 \(\operatorname{bh}\)。依据兄弟与叔侄关系判断
兄弟为红色,只需要花费一次旋转操作,便可以归结为其他情况。
兄弟为黑色,且两个侄子也都是黑色,“将自己和兄弟的黑色传给父亲”。如果父亲是红色,则将父亲变为黑色,兄弟变为红色;如果父亲是黑色,则兄弟变为红色,并将情况递归到父亲。无需旋转。
兄弟为黑色,近侄子为红色,远侄子为黑色,“这样没法传黑色”。需要先将近侄子变为黑色,兄弟变为红色,然后进行一次右旋转,转变为 Case 4。(近侄子和兄弟的颜色变化不会影响内部的 \(\operatorname{bh}\))
兄弟为黑色,远侄子为红色,“最复杂的一种情况”。因为常规思路是哪里不平衡需要减轻其不平衡程度,但是这里的反而先加重了,因为最后会将“叶子结点”删去,需要有结点去平衡其删除后的影响。首先将父亲和兄弟的颜色互换,并且将远侄子变为黑色。然后对父亲进行一次左旋。注意到 \(\operatorname{bh}(c) = \operatorname{bh}(d) = 0\),且 \(\operatorname{bh}(b) = 1\),所以左旋后的树各结点的 \(\operatorname{bh}\) 不变。
B+ 树 ¶
B+ 树是一种多路搜索树,其定义满足以下性质:
- 根结点要么是叶子结点,要么有 \(2\) 到 \(M\) 个子结点,其中 \(M\) 是树的阶数;
- 除根结点外的所有非叶子结点有 \(\lceil M/2 \rceil\) 到 \(M\) 个子结点;
- 所有叶子结点拥有相同的深度
。 (假设所有的非根的叶子结点也有 \(\lceil M/2 \rceil\) 到 \(M\) 个子结点,子结点指键值)
插入 ¶
#ifndef _BP_TREE_H_
#define _BP_TREE_H_
#define MAX 3
#define MAXN 10005
typedef struct node *Tree;
typedef struct node
{
int size;
int isLeaf;
int key[MAX + 1];
Tree parent;
Tree child[MAX + 1];
} Node;
int InsertValue(Tree p, int key);
Tree InsertLeaf(Tree root, int key);
Tree SplitLeaf(Tree p, Tree parent, Tree root, int key);
Tree InsertInternal(Tree p, Tree Lefttree, Tree Righttree, Tree root, int key);
void PrintTree(Tree root);
#endif
Tree InsertLeaf(Tree root, int key) {
if (!root) {
root = (Tree)malloc(sizeof(Node));
root->size = 1;
root->isLeaf = 1;
root->key[0] = key;
root->parent = NULL;
for (int i = 0; i < MAX; i++) {
root->child[i] = NULL;
}
} else {
Tree p = root;
Tree parent = NULL;
while (!p->isLeaf) {
parent = p;
for (int i = 0; i < p->size; i++) {
if (key < p->key[i]) {
p = p->child[i];
break;
}
if (i == p->size - 1) {
p = p->child[i + 1];
break;
}
}
}
if (p->size < MAX) {
if (InsertValue(p, key) == -1) {
return root;
}
p->parent = parent;
} else {
root = SplitLeaf(p, parent, root, key);
}
}
return root;
}
Tree SplitLeaf(Tree p, Tree parent, Tree root, int key) {
Tree LeftTree = (Tree)malloc(sizeof(Node));
Tree RightTree = (Tree)malloc(sizeof(Node));
if (InsertValue(p, key) == -1) {
return root;
}
LeftTree->size = (MAX + 1) / 2;
RightTree->size = MAX + 1 - (MAX + 1) / 2;
LeftTree->isLeaf = RightTree->isLeaf = 1;
for (int i = 0; i < LeftTree->size; i++) {
LeftTree->key[i] = p->key[i];
}
for (int i = 0; i < RightTree->size; i++) {
RightTree->key[i] = p->key[i + LeftTree->size];
}
if (p == root) {
Tree newRoot = (Tree)malloc(sizeof(Node));
newRoot->size = 1;
newRoot->key[0] = RightTree->key[0];
newRoot->isLeaf = 0;
newRoot->child[0] = LeftTree;
newRoot->child[1] = RightTree;
root = newRoot;
LeftTree->parent = RightTree->parent = newRoot;
} else {
root = InsertInternal(parent, LeftTree, RightTree, root, RightTree->key[0]);
}
return root;
}
Tree InsertInternal(Tree p, Tree LeftTree, Tree RightTree, Tree root, int key) {
if (p->size < MAX - 1) { // insert directly
int i = InsertValue(p, key);
if (i == -1) {
return root;
}
for (int j = p->size; j > i + 1; j--) {
p->child[j] = p->child[j - 1];
}
p->child[i] = LeftTree;
p->child[i + 1] = RightTree;
LeftTree->parent = RightTree->parent = p;
} else { // split internal
Tree LeftChild = (Tree)malloc(sizeof(Node));
Tree RightChild = (Tree)malloc(sizeof(Node));
Tree ptrarray[MAX + 1];
for(int i = 0; i < MAX + 1; i++) {
ptrarray[i] = NULL;
}
for(int i = 0; i < MAX + 1; i++) {
ptrarray[i] = p->child[i];
}
int i = InsertValue(p, key);
if (i == -1) {
return root;
}
for (int j = MAX + 1; j > i + 1; j--) {
ptrarray[j] = ptrarray[j - 1];
}
ptrarray[i] = LeftTree;
ptrarray[i + 1] = RightTree;
LeftChild->isLeaf = RightChild->isLeaf = 0;
// the size is number of children - 1
LeftChild->size = (MAX + 1) / 2 - 1;
RightChild->size = (MAX + 1) - (MAX + 1) / 2 - 1;
// note that the most left branch of right child dose not need a key
for (int i = 0; i < LeftChild->size; i++) {
LeftChild->key[i] = p->key[i];
}
for (int i = 0; i < RightChild->size; i++) {
RightChild->key[i] = p->key[i + LeftChild->size + 1];
}
for (int i = 0; i < LeftChild->size + 1; i++) {
LeftChild->child[i] = ptrarray[i];
LeftChild->child[i]->parent = LeftChild;
}
for (int i = 0; i < RightChild->size + 1; i++) {
RightChild->child[i] = ptrarray[i + LeftChild->size + 1];
RightChild->child[i]->parent = RightChild;
}
if (p == root) {
Tree newRoot = (Tree)malloc(sizeof(Node));
newRoot->size = 1;
newRoot->key[0] = p->key[LeftChild->size];
newRoot->isLeaf = 0;
newRoot->child[0] = LeftChild;
newRoot->child[1] = RightChild;
root = newRoot;
LeftChild->parent = RightChild->parent = newRoot;
} else {
root = InsertInternal(p->parent, LeftChild, RightChild, root, p->key[LeftChild->size]);
}
}
return root;
}