1.红黑树
(1)红黑树的实现
cpp
#include <iostream>
using namespace std;
// 红黑树实现
template <typename T>
class RBTree
{
public:
RBTree() : root_(nullptr) {}
~RBTree() {}
// 插入
void insert(const T& val)
{
// 当红黑树为空插入根节点
if (root_ == nullptr)
{
root_ = new Node(val);
return;
}
// 当红黑树不为空
Node* parent = nullptr;
Node* cur = root_;
while (cur != nullptr)
{
if (cur->data_ > val)
{
parent = cur;
cur = cur->left_;
}
else if (cur->data_ < val)
{
parent = cur;
cur = cur->right_;
}
else
{
return;
}
}
// 插入节点
Node *node = new Node(val, nullptr, nullptr, parent, RED);
if (parent->data_ > val)
{
parent->left_ = node;
}
else
{
parent->right_ = node;
}
// 插入完成调整节点以满足红黑树性质
if (color(parent) == RED)
{
fixAfterInsert(node);
}
}
// 删除
void remove(const T& val)
{
// 红黑树为空
if (root_ == nullptr)
{
return;
}
// 红黑树不为空
// 找到要删除的节点
Node* cur = root_;
while (cur != nullptr)
{
if (cur->data_ < val)
{
cur = cur->right_;
}
else if (cur->data_ > val)
{
cur = cur->left_;
}
else
{
break;
}
}
// 没有找到要删除的节点
if (cur == nullptr)
{
return;
}
// 找到了要删除的节点
// 要删除的节点是情况三->该节点有左右孩子->删除其前驱或后继
if (cur->left_ != nullptr && cur->right_ != nullptr)
{
Node* pre = cur->left_;
while (pre->right_ != nullptr)
{
pre = pre->right_;
}
// 找到要删除的前驱节点
cur->data_ = pre->data_;
cur = pre; // 更新当前要删除的节点,转化为情况一和二
}
// 要删除的节点是情况一二
Node* child = cur->left_;
if (child == nullptr)
{
child = cur->right_;
}
// 要删除的节点是情况二->有一个孩子节点
if (child != nullptr)
{
child->parent_ = cur->parent_;
if (child->parent_ == nullptr)
{
root_ = child;
}
else
{
if (cur->parent_->left_ == cur)
{
cur->parent_->left_ = child;
}
else
{
cur->parent_->right_ = child;
}
}
Color c = color(cur);
delete cur;
if (c == BLACK)
{
fixAfterRemove(child);
}
}
// 要删除的节点是情况一->没有孩子节点
else
{
if (cur->parent_ == nullptr)
{
delete cur;
root_ == nullptr;
return;
}
else
{
if (color(cur) == BLACK)
{
fixAfterRemove(cur);
}
if (cur->parent_->left_ == cur)
{
cur->parent_->left_ = nullptr;
}
else
{
cur->parent_->right_ = nullptr;
}
delete cur;
}
}
}
private:
// 红黑树颜色
enum Color
{
BLACK,
RED
};
// 红黑树的节点
struct Node
{
Node(T data = T(), Node* left = nullptr, Node* right = nullptr, Node* parent = nullptr, Color color = BLACK)
: data_(data), left_(left), right_(right), parent_(parent), color_(color)
{
}
T data_;
Node* left_;
Node* right_;
Node* parent_;
Color color_;
};
// 指向红黑树根节点的指针
Node* root_;
// 获取当前节点的父节点
Node* parent(Node* node)
{
return node->parent_;
}
// 获取当前节点的左孩子
Node* left(Node* node)
{
return node->left_;
}
// 获取当前节点的右孩子
Node* right(Node* node)
{
return node->right_;
}
// 获取当前节点的颜色
Color color(Node* node)
{
if (node == nullptr)
{
return BLACK;
}
return node->color_;
}
// 设置当前节点的颜色
void setcolor(Node* node, Color color)
{
node->color_ = color;
}
// 右旋操作
void rightRotate(Node* node)
{
Node* child = node->left_;
child->parent_ = node->parent_;
if (node->parent_ == nullptr)
{
root_ = child;
}
else
{
if (node->parent_->right_ == node)
{
node->parent_->right_ = child;
}
else
{
node->parent_->left_ = child;
}
}
node->left_ = child->right_;
if (child->right_ != nullptr)
{
child->right_->parent_ = node;
}
child->right_ = node;
node->parent_ = child;
}
// 左旋操作
void leftRotate(Node* node)
{
Node* child = node->right_;
child->parent_ = node->parent_;
if (node->parent_ == nullptr)
{
root_ = child;
}
else
{
if (node->parent_->left_ == node)
{
node->parent_->left_ = child;
}
else
{
node->parent_->right_ = child;
}
}
node->right_ = child->left_;
if (child->left_ != nullptr)
{
child->left_->parent_ = node;
}
child->left_ = node;
node->parent_ = child;
}
// 插入节点后调整程序
void fixAfterInsert(Node* node)
{
// 当前节点的父亲节点是红色,需要进行调整
while (color(parent(node)) != BLACK)
{
// 判断当前节点的父亲节点是其爷爷节点的左孩子还是右孩子
if (parent(parent(node))->left_ == parent(node))
{
// 当前节点的父亲节点在其爷爷节点的左边
// 定义叔叔节点
Node* uncle = parent(parent(node))->right_;
// 三种情况判断
if (color(uncle) == RED)
{
// 第一种情况->叔叔节点和父亲节点都为红色
setcolor(parent(node), BLACK);
setcolor(uncle, BLACK);
setcolor(parent(parent(node)), RED);
node = parent(parent(node));
}
else
{
// 第三种情况判断
if (parent(node)->right_ == node)
{
// 第三种情况->叔叔节点是黑色当前接点插入到了父亲节点的右边
// 当前节点的父亲节点为根节点左旋
node = parent(node);
leftRotate(node); // 旋转后转化为第二种情况处理
}
// 第二种情况->叔叔节点是黑色当前节点插入到了父亲节点的左边
// 以当前节点的爷爷节点为根节点进行右旋
setcolor(parent(node), BLACK);
setcolor(parent(parent(node)),RED);
rightRotate(parent(parent(node)));
break; //当以第二种情况进行调整后局部就调整完成整个也调整完成可以退出循环了
}
}
else
{
// 当前节点的父亲节点在其爷爷节点的右边
// 定义叔叔节点
Node* uncle = parent(parent(node))->left_;
// 三种情况判断
if (color(uncle) == RED)
{
// 第一种情况->叔叔节点和父亲节点都为红色
setcolor(parent(node), BLACK);
setcolor(uncle, BLACK);
setcolor(parent(parent(node)), RED);
node = parent(parent(node));
}
else
{
// 第三种情况判断
if (parent(node)->left_ == node)
{
// 第三种情况->叔叔节点是黑色当前接点插入到了父亲节点的左边
// 当前节点的父亲节点为根节点右旋
node = parent(node);
rightRotate(node); // 旋转后转化为第二种情况处理
}
// 第二种情况->叔叔节点是黑色当前节点插入到了父亲节点的右边
// 以当前节点的爷爷节点为根节点进行左旋
setcolor(parent(node), BLACK);
setcolor(parent(parent(node)), RED);
leftRotate(parent(parent(node)));
break;
}
}
}
// 将根节点强制转化为黑色
setcolor(root_, BLACK);
}
// 删除后调整程序
void fixAfterRemove(Node* node)
{
// 当前节点不是红色
while (node != root_ && color(node) != RED)
{
// 当前节点是父节点的左孩子
if (left(parent(node)) == node)
{
// 获取兄弟节点
Node* brother = right(parent(node));
// 情况四->兄弟节点是红色
if (color(brother) == RED)
{
setcolor(brother, color(parent(node)));
setcolor(parent(node),RED);
leftRotate(parent(node));
// 更新brother
brother = right(parent(node)); // 转化为了前三种情况
}
// 情况三->兄弟节点是黑色,兄弟节点的两个孩子都是黑色
if (color(left(brother)) == BLACK && color(right(brother)) == BLACK)
{
setcolor(brother,RED);
node = parent(node);
}
// 判断情况一和二
else
{
// 情况二->兄弟节点是黑色,兄弟节点的右孩子是黑色,左孩子是红色
if (color(right(brother)) == BLACK)
{
rightRotate(brother);
setcolor(brother,RED);
setcolor(parent(brother),BLACK);
// 更新brother
brother = parent(brother);
}
// 都归结为情况一进行处理
// 情况一->兄弟节点是黑色,兄弟节点的有孩子是红色
if (color(right(brother)) == RED)
{
setcolor(brother, color(parent(brother)));
setcolor(parent(brother), BLACK);
setcolor(right(brother), BLACK);
leftRotate(parent(brother));
break;
}
}
}
// 当前节点是父节点的右孩子
else
{
// 获取兄弟节点
Node* brother = left(parent(node));
// 情况四->兄弟节点是红色
if (color(brother) == RED)
{
setcolor(brother, color(parent(node)));
setcolor(parent(node), RED);
rightRotate(parent(node));
// 更新brother
brother = left(parent(node)); // 转化为了前三种情况
}
// 情况三->兄弟节点是黑色,兄弟节点的两个孩子都是黑色
if (color(left(brother)) == BLACK && color(right(brother)) == BLACK)
{
setcolor(brother,RED);
node = parent(node);
}
// 判断情况一和二
else
{
// 情况二->兄弟节点是黑色,兄弟节点的左孩子是黑色,右孩子是红色
if (color(left(brother)) == BLACK)
{
leftRotate(brother);
setcolor(brother,RED);
setcolor(parent(brother),BLACK);
// 更新brother
brother = parent(brother);
}
// 都归结为情况一进行处理
// 情况一->兄弟节点是黑色,兄弟节点的左孩子是红色
if (color(left(brother)) == RED)
{
setcolor(brother, color(parent(brother)));
setcolor(parent(brother), BLACK);
setcolor(right(brother), BLACK);
rightRotate(parent(brother));
break;
}
}
}
}
// 当前节点是红色
setcolor(node,BLACK);
}
};
在进行插入节点是第三种情况时即插入节点插入到父亲节点的右边,那么就可以将其转化为第二种情况即插入节点插入到父亲节点的左边进行统一处理,对第二种情况处理后我们所得到的就已经时局部满足红黑树了即整体满足红黑树了,就可以退出循环啦。
(2)相关知识
红黑树要满足的性质:
1.每一个节点必须有颜色,不是红色就是黑色,空表示黑色
2.根节点必须是黑色
3.在红黑树中不能出现连续的红色节点
4.从根节点出发到每一个叶子节点所经过的路径的黑色节点数量要一样。
2.B树,B+树
(1)相关知识
共同点:
B树和B+树也是一颗平衡树,且其叶子节点都在同一层上。B树和B+树的搜索和前面所说的搜索树的搜索方式类似。
不同点:
B树的节点:对于一个m阶的B树其节点的最多有m个分支,m-1个元素。最少有大于等于m/2最小整数个分支,大于等于m/2最小整数减1个元素。
B树的插入:通过搜索找到要插入的位置,当插入数据后不超过其最大元素个数直接插入即可,当插入后超过其最大元素个数则以其m/2向上取整整数位置将改节点分成两点数据,其m/2向上取整整数位置的值上升到其原来节点的父亲节点,若上升后父亲节点元素个数超过最大元素个数则重复以上步骤。
B树的删除:通过比较找到要删除的元素的位置,若删除后节点元素数量不小于其最小的元素个数直接删除即可,若删除后小于节点最小元素个数则首先看其兄弟节点是否大于最小元素个数,若大于就从其兄弟节点借一个元素,具体操作就是若改兄弟节点在其左边则首先将其父亲节点中在其两节点间的元素下沉到需要借元素的节点中,然后将其左边兄弟节点的最大元素上升到其父亲节点中,右边节点的话按照类似的处理即可。若其兄弟节点不能借元素,则将其兄弟节点与其和他们的父亲节点中他们两之间的元素进行合并,若合并后其父亲节点的元素个数少于最少元素个数了则进行同样操作即可。
B+树的节点:B+树常用于文件搜索数据库索引,其叶子节点存有数据的指针,且其叶子节点不仅是树的叶子节点还是一串链表可以通过链表来遍历搜索。对于m阶B+树最大的分支数为m最大的元素个数也为m,最小的分支数和元素为m/2的向上取整。
B+树的查询删除插入操作:B+树由于其特殊的结构,B+树的查询可以实现随机查找,顺序查找和范围查找,B+树的删除和插入操作则与B树类似。