红黑树性质
1.不能有连在一起的红色节点
2. 每个红色节点的子节点都是黑色
3. 叶子结点都是黑色
4. 根节点都是黑色
5. 对每个结点,从该结点到其后代叶结点的简单路径上,均包含相同数目的黑色结点。
为了满足这些性质,最后就形成AVL,但是其实并不严格,就是说它的左右子树深度差可以大于1,但是正是因为这样,而让旋转更加容易,因此它的缓冲平衡机制就更容易实现。
红黑树中最长支路的长度必然“不大于”最短者的2倍,所以不妨设Max者为H,Min者为h。则有:H<=2h。
但是其实考虑到在操作过程中还有一部上色操作,其实跟普通的AVL操作复杂度差不多,但是又考虑到红黑树更容易实现,因此在C++中map和set底层还是用的红黑树;
红黑树的旋转
红黑树一共又三种平衡机制: 变色,左旋,右旋;
这里我们用一种很经典的例子来概括三种变换方式;
我们有一颗这样的红黑树
然后我们想插入一个节点6,由于查找二叉树的性质,我们必须这样插入
值得注意的是,插入的点都必须默认为红色
我们发现下方6,和7连续为红色,因此我们首先变色变成这样
将连续的红色中的一个和其父结点的黑色颜色互换(如果是父结点就与其另外一个子节点换颜色)
然后我们发现还是又12和5连续为红色,这个时候如果再变色是不行的了,因为再变的话根节点就不是黑色了。
由于连续的在左边,因此我们需要用到左旋,变成这样
我们先不看19以及右边那一坨,先以只以左子树为准,我么不难发现就是以7及其下面对依托为旋转中心,这一坨不动,其余的向左向左旋转了一圈,旋转之后,12就变成了根,然后我们再讲7这一坨挂到左边就行。
这就是所谓的左旋
左旋之后,我们发现,其实还是没有满足红黑树性质要求,又因为连续的在右边,这个时候我们需要右旋,变成这样
其实也就是以13为旋转中心向右旋转一圈,这个时候,12就变成了根节点,然后我们再把13挂到右边一坨就行
然后我们发现其实还是没有满足要求,这个时候我们还需要再来一次颜色的改变变成这样
这个时候就平衡了
以左旋为例
enum Color
{
R,
B,
};
struct BRNode
{
int data;
int color = R;
BRNode *left;
BRNode *right;
BRNode *father;
BRNode(int data) : data(data){};
};
class BRtree
{
private:
static BRNode *root;
public:
void myinsert(int innum, BRNode* node = root)//应该不允许重复
{
if (root->data < innum)
{
if (root->right == NULL)
{
root->right = new BRNode(innum);
}
else
{
myinsert(innum, root->right);
}
}
else
{
if (root->left == NULL)
{
root->left = new BRNode(innum);
}
else
{
myinsert(innum, root->left);
}
}
}
void leftRotate(BRNode* node)
{
if (node->father == NULL)
{
BRNode *E = root;
BRNode* S = E->right;
//将下面一坨移到左边
E-> right = S->left;
S->left->father = E;
//旋转节点
S->left = E;
E->father = S;
S->father = NULL;
}
else
{
if (node == node->father->left)//如果要旋转的中心是父结点的左子树根
{
node->father->left = node->right;//重新定义子树根节点
}
else
{
node->father->right = node->right;
}
//更改新的次根节点的父结点
node->right->father = node->father;
//更改本结点的父结点
node->father = node->right;
node->right->left = node;
//重新挂载旋转中心
node->right = node->right->left;
node->right->father = node;
}