平衡二叉树
平衡二叉树的定义:它的左右树高度差不超过1
为什么需要平衡二叉树?一种极端的情况:二叉搜索树的结点为1、2、3、4、5,如下图,这样的二叉树退化成链表,查找一个节点的时间复杂度是O(n)。为了避免这种情况,我们将它转化成平衡二叉排序树,二叉排序树的查找平均复杂度是O(log(n))
我们该如何进行优化呢?因此有了平衡二叉树和红黑树。平衡二叉树定义左右树高度差不超过1,这样可以有效的避免直线型结构,但是并不是我们最理想的状态。
红黑树性质
红黑树是每个节点都带有颜色属性的二叉查找树,颜色或红色或黑色。在二叉查找树强制一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求:
- 节点是红色或黑色。
- 根节点是黑色。
- 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
- 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
旋转和变换规则
1、颜色变化情况:当前节点的父节点是红色,且祖父节点的另一个子节点(叔叔节点)也是红色
把父节点变成黑色;把祖父节点的另一个节点变成黑色;把祖父节点变成红色;把指针定义到祖父节点设置成当前操作的。
2、左旋:父节点为红色,叔叔节点为黑色的时候,且当前节点为右子树。以父节点作为左旋。
3、右旋:父节点为红色,叔叔节点为黑色的时候,且当前节点为左子树。以父节点作为右旋。
把父节点变成黑色;祖父节点变成红色;把祖父节点旋转。
左旋如图
右旋效果图
Java代码实现
public class RedBlackTree<T extends Comparable<T>> {
private RedBlackTreeNode<T> root; // 根结点
private static final int RED = 0;
private static final int BLACK = 1;
public class RedBlackTreeNode<T extends Comparable<T>> {
int color;
T key;
// 左节点
RedBlackTreeNode<T> left;
// 右节点
RedBlackTreeNode<T> right;
// 父结点
RedBlackTreeNode<T> parent;
public RedBlackTreeNode(T key, int color, RedBlackTreeNode<T> parent, RedBlackTreeNode<T> left,
RedBlackTreeNode<T> right) {
this.key = key;
this.color = color;
this.parent = parent;
this.left = left;
this.right = right;
}
}
}
左旋
private void leftRotate(RedBlackTreeNode<T> x) {
// 设置x的右孩子为y
RedBlackTreeNode<T> y = x.right;
// 将 “y的左孩子” 设为 “x的右孩子”;
x.right = y.left;
// 如果y的左孩子非空,将 “x” 设为 “y的左孩子的父亲”
if (y.left != null)
y.left.parent = x;
// 将 “x的父亲” 设为 “y的父亲”
y.parent = x.parent;
if (x.parent == null) {
// 如果 “x的父亲” 是空节点,则将y设为根节点
this.root = y;
} else {
// 如果 x是它父节点的左孩子,则将y设为“x的父节点的左孩子”
if (x.parent.left == x) {
x.parent.left = y;
} else {
// 如果 x是它父节点的左孩子,则将y设为“x的父节点的左孩子”
x.parent.right = y;
}
}
// 将 “x” 设为 “y的左孩子”
y.left = x;
// 将 “x的父节点” 设为 “y”
x.parent = y;
}
右旋
private void rightRotate(RedBlackTreeNode<T> y) {
// 设置x是当前节点的左孩子。
RedBlackTreeNode<T> x = y.left;
// 将 “x的右孩子” 设为 “y的左孩子”;
y.left = x.right;
// 如果"x的右孩子"不为空的话,将 “y” 设为 “x的右孩子的父亲”
if (x.right != null)
x.right.parent = y;
// 将 “y的父亲” 设为 “x的父亲”
x.parent = y.parent;
if (y.parent == null) {
this.root = x; // 如果 “y的父亲” 是空节点,则将x设为根节点
} else {
if (y == y.parent.right)
y.parent.right = x; // 如果 y是它父节点的右孩子,则将x设为“y的父节点的右孩子”
else
y.parent.left = x; // (y是它父节点的左孩子) 将x设为“x的父节点的左孩子”
}
// 将 “y” 设为 “x的右孩子”
x.right = y;
// 将 “y的父节点” 设为 “x”
y.parent = x;
}
参考链接
https://www.cnblogs.com/zedosu/p/6635034.html