红黑树定义和性质
红黑树是一种含有红黑结点并能自平衡的二叉查找树。它必须满足下面性质:
- 性质1:每个节点要么是黑色,要么是红色。
- 性质2:根节点是黑色。
- 性质3:每个叶子节点(NIL)是黑色。(这里的叶子节点是指空叶子节点)
- 性质4:每个红色结点的两个子结点一定都是黑色。
- 性质5:任意一结点到每个叶子结点的路径都包含数量相同的黑结点。
红黑树是一棵近似平衡的二叉搜索树,它查找、插入和删除的时间复杂度都是O(lg n);
红黑树的自平衡
插入和删除会破坏红黑树的平衡,需要靠三种操作来维持:左旋、右旋和变色。旋转的时候维持二叉搜索树的性质就好(左子结点比根节点小,右子节点比根节点大)
左旋和右旋的意义就是让违反性质4的节点不断上移到根节点,然后让根节点变成黑色(避免违反性质5),这样就可以达到自平衡
- 左旋:对x进行左旋,意味着,将“x的右孩子”设为“x的父亲节点”;即,将 x变成了一个左节点(x成了为z的左孩子)!。 因此,左旋中的“左”,意味着“被旋转的节点将变成一个左节点”。
左旋示例图(以x为节点进行左旋):
z x / / \ --(左旋)--> x y z / y
/* * 左旋示意图(对节点x进行左旋): * * * px px * / / * x y * / \ --(左旋)--> / \ # * lx y x ry * / \ / \ * ly ry lx ly * * 三处变化: * 1、被旋转的节点 变成 左节点 * 2、被旋转节点的右节点 变成 根节点 * 3、被旋转节点的右节点的左节点 变成 被旋转节点的右节点 * * 代码实现的时候,修改这种变化从第3点开始修改(也就是最下面的变化开始往上修改) */ //以x为支点进行左旋 void LeftRotate(Tree* tree, node* x) { //新建节点y,保存x的右节点 node* y = x->right; //将被旋转节点的右节点的左节点 设置为 被旋转节点的右节点 x->right = y->left; //更新被旋转节点x的 右节点的左节点 的父节点 if (y->left != NULL) y->left->parent = x; //更新旋转后 y的父节点 y->parent = x->parent; //判断x为根节点的情况,那么旋转后y变成根节点 if (x->parent == NULL) tree->root = y; else { //如果x是它父节点的左儿子,旋转后这个父节点的左儿子就是y if (x == x->parent->left) x->parent->left = y; else x->parent->right = y; } y->left = x; x->parent = y; }
- 右旋:对x进行右旋,意味着,将“x的左孩子”设为“x的父亲节点”;即,将 x变成了一个右节点(x成了为y的右孩子)! 因此,右旋中的“右”,意味着“被旋转的节点将变成一个右节点”。
右旋示例图(以x为节点进行右旋):
y x \ / \ --(右旋)--> x y z \ z
/* * 右旋示意图(对节点y进行左旋): * * * py py * / / * y x * / \ --(右旋)--> / \ # * x ry lx y * / \ / \ # * lx rx rx ry * * 三处变化: * 1、被旋转的节点 变成 右节点 * 2、被旋转节点的左节点 变成 根节点 * 3、被旋转节点的左节点的右节点 变成 被旋转节点的左节点 */ //以y为支点进行左旋 void RightRotate(Tree* tree, node* y) { //新建x节点保存被旋转节点的左节点 node* x = y->left; //将被旋转节点的左节点的右节点 设置为 被旋转节点的左节点 y->left = x->right; //更新被旋转节点y的 左节点的右节点 的父节点 if (x->right != NULL) x->right->parent = y; //更新旋转后 x的父节点 x->parent = y->parent; //判断y为根节点的情况,那么旋转后x变成根节点 if (y->parent == NULL) tree->root = x; else { //如果被旋转节点y是它父节点的左儿子,旋转后这个父节点的左儿子就是x if (y == y->parent->right) y->parent->right = x; else y->parent->left = x; } x->right = y; y->parent = x; }
- 变色:结点的颜色由红变黑或由黑变红。