红黑树

前言

  红黑树作为Java、Android种经常用到的一种数据结构,我们还是很有必要对其进行了解的。

  红黑树是一棵非严格均衡的二叉树,均衡二叉树又是在二叉搜索树的基础上增加了自动维持平衡的性质,插入、搜索、删除的效率都比较高。红黑树也是实现 TreeMap、HashMap 存储结构的基石。

二叉树

什么是二叉树?

  二叉树是每个结点最多有两个子树的树结构。

  比较常见的二叉树类型有完全二叉树、满二叉树、二叉搜索树、均衡二叉树等。

  红黑树就是一颗二叉搜索树,也叫二叉查找树、二叉排序树。

二叉搜索树有如下几个特点:

  节点的左子树小于节点本身

  节点的右子树大于节点本身

  左右子树同样为二叉搜索树

  下图就是一棵典型的二叉搜索树:

二叉搜索树

  二叉搜索树是均衡二叉树的基础,它基于二叉查找算法去查找一个目标数,最高可以把查找效率提高到O(logn)。

  为什么是最高?可以看一下下面的二叉搜索树。

  这样的二叉搜索树接近一条链表,查找效率最差接近O(n),所以我们需要对它进行平衡处理。

  这样的就将性能提高了很多,而红黑树就是可以自动调节的非严格均衡的二叉搜索树。

红黑树规则特点

  • 根节点必为黑色。

  • 叶子节点都为黑色,且为 null。

  • 连接红色节点的两个子节点都为黑色(红黑树不会出现相邻的红色节点)。

  • 从任意节点出发,到其每个叶子节点的路径中包含相同数量的黑色节点。

  • 新加入到红黑树的节点为红色节点。

红黑树

从黑红树的规则中可以看出:

  ①纯黑色节点组成的路径为最短路径,红黑节点数相同的路径为最长路径。

  ②从根节点到叶子节点的最长路径不大于最短路径的 2 倍。

  ③新加入节点为黑色必定破坏平衡,为红色则降低破坏的可能性。

维持平衡的方式有两种:

  1.【变色】
  2.【旋转】包括【左旋】和【右旋】

下面我们从插入和删除两种场景来举例说明:

红黑树节点插入

Case 无需调整 [变色]即可实现平衡 [旋转+变色]才可实现平衡
1 当父节点为黑色时插入子节点 空树插入根节点 将根节点红色变为黑色父节点 父节点为红色左节点,叔父节点为黑色,插入左子节点,那么通过[左左节点旋转]
2 父节点和叔父节点都为红色 父节点为红色左节点,叔父节点为黑色,插入右子节点,那么通过[左右节点旋转]
3 父节点为红色右节点,叔父节点为黑色,插入左子节点,那么通过[右左节点旋转]
4 父节点为红色右节点,叔父节点为黑色,插入右子节点,那么通过[右右节点旋转]

变色:红色变黑色或者黑色变红色。

左旋:逆时针旋转,S节点代替E节点的位置,E节点成为S节点的左孩子,S节点的左孩子成为E节点的右孩子。

左旋
private void rotateLeft(TreeMapEntry p) {
    if (p != null) {
        TreeMapEntry r = p.right;
        p.right = r.left;
        if (r.left != null)
            r.left.parent = p;
        r.parent = p.parent;
        if (p.parent == null)
            root = r;
        else if (p.parent.left == p)
            p.parent.left = r;
        else
            p.parent.right = r;
        r.left = p;
        p.parent = r;
    }
}

右旋:顺时针旋转,E节点代替S节点的位置,S节点成为E节点的右孩子,E节点的右孩子成为S节点的左孩子。

右旋
private void rotateRight(TreeMapEntry p) {
    if (p != null) {
        TreeMapEntry l = p.left;
        p.left = l.right;
        if (l.right != null) l.right.parent = p;
        l.parent = p.parent;
        if (p.parent == null)
            root = l;
        else if (p.parent.right == p)
            p.parent.right = l;
        else p.parent.left = l;
        l.right = p;
        p.parent = l;
    }
}

左右->以父节点左旋不用变色

右左->以父节点右旋不用变色

左左->以祖父节点右旋加变色

右右->以祖父节点左旋加变色

红黑树节点删除

相比较于红黑树的节点插入,删除节点更为复杂,我们从子节点是否为 null 和红色为思考维度来讨论。

Case 子节点都为null 有一个子节点为null 两个子节点都不为null
1 删除节点后将当前位置置为null 删除节点后将其子节点取代当前节点 如果删除的是左节点,则将前驱的值复制到该节点中,然后删除前驱
2 如果删除的是右节点,则将后继的值复制到该节点中,然后删除后继

前驱:左子树中值最大的节点(可得出其最多只有一个非 null 子节点,可能都为 null)。

后继:右子树中值最小的节点(可得出其最多只有一个非 null 子节点,可能都为 null)。

前驱和后继都是值最接近该节点值的节点,类似于该节点.prev=前驱,该节点.next=后继。

Reference:
https://mp.weixin.qq.com/s/rDPMn38pGrcz6DEKWER_mQ

你可能感兴趣的:(红黑树)