八大数据结构——红黑树(七)

八大数据结构——红黑树(七)


树是数据结构中非常重要的一项,有关树的数据结构有许多种,本文重点研究的是红黑树。

关于红黑树,是二叉树范畴里的高级数据结构,内容十分的多,网上各种文章参差不齐,经过我的血泪史,读者可以通过以下文章,完整的了解红黑树,并且最后附上Java实现代码。

二叉搜索树

红黑树是建立在二叉搜索树基础上的,如果您还不了解,请看 八大数据结构——二叉搜索树(七)

2-4树

在学习红黑树之前,您需要搞懂:红黑树中最重要的,红与黑两种颜色代表什么?为什么要用标上红与黑这两种颜色?
通过了解2-4树(您不一定要完全掌握2-4树),来理解红黑树。请看 通过2-3-4树理解红黑树

红黑树的定义和插入

通过该篇文章,您可以初步了解红黑树的定义和插入操作。请看 彻底理解红黑树(二)之 插入

红黑树的删除

红黑树的删除是其重点与难点,涉及的情况非常多,如果您一时无法明白,可以结合下面的实现代码理解。请看 彻底理解红黑树(三)之 删除

红黑树演示网站

有一个网站,非常不错,可以动态演示很多常用的数据结构,您可以通过该网站直观地了解红黑树的插入和删除操作。请看 红黑树演示网站
如果您还想了解其他的数据结构动画,可以点击它们的目录 演示网站目录

红黑树与AVL树

学习过AVL树的都知道,AVL树的平衡程度更高于红黑树,为什么许多的实际数据结构应用,还是使用了红黑树呢?

  1. 著名的linux进程调度Completely Fair Scheduler,用红黑树管理进程控制块
  2. epoll在内核中的实现,用红黑树管理事件块
  3. nginx中,用红黑树管理timer等
  4. Java的TreeMap实现

请看 红黑树与AVL树,各自的优缺点总结

面试中关于红黑树

面试常问:什么是红黑树?

代码实现

洋洋洒洒地学完那么多,来看看Java代码实现吧。您可以对照前面的文章来看代码,就会好理解很多,代码中给出了一些重要注释,也方便您的理解。

//红黑树
public class RedBlackTree<T extends Comparable> {
    private RBNode<T> root;
    private int size;

    public RedBlackTree() {
        root = null;
        size = 0;
    }

    public RedBlackTree(T val) {
        root = new RBNode<>();
        root.color = Color.black;
        root.val = val;
        size = 1;
    }

    public boolean empty() {
        return size == 0;
    }

    public int size() {
        return size;
    }

    //    插入节点
    public boolean insert(T val) {
        //        如果val为null直接返回
        if (val == null) {
            return false;
        }
        //        如果root为null,那新建一个节点,并让保存为root
        if (root == null) {
            root = new RBNode<>();
            root.val = val;
            root.color = Color.black;
            size++;
            return true;
        }
        //        root不为null,那就通过循环找到正确的插入位置
        RBNode<T> node = root;
        while (true) {
            //            如果val与已有值相等,则不允许插入
            if (node.val.compareTo(val) == 0) {
                return false;
            }
            //            val值小于当前节点,则插入到左子树
            if (node.val.compareTo(val) > 0) {
                //                如果左子节点为空,则插入到此处
                if (node.leftNode == null) {
                    RBNode<T> leftNode = new RBNode<>();
                    leftNode.val = val;
                    leftNode.color = Color.red;
                    node.leftNode = leftNode;
                    adjustInsert(leftNode);
                    size++;
                    return true;
                } else {
                    //                    不为空继续往下找
                    node = node.leftNode;
                }
                //                val值大于当前节点,则插入到右子数
            } else {
                //                如果右子节点为空,则插入到此处
                if (node.rightNode == null) {
                    RBNode<T> rightNode = new RBNode<>();
                    rightNode.val = val;
                    rightNode.color = Color.red;
                    node.rightNode = rightNode;
                    adjustInsert(rightNode);
                    size++;
                    return true;
                } else {
                    //                    不为空继续往下找
                    node = node.rightNode;
                }
            }
        }
    }

    //    删除节点
    public boolean remove(T val) {
        RBNode<T> node = find(val);
        if (node == null) {
            return false;
        }
        //        要删除的节点有三种情况,度为2,度为1和度为0的。
        //        平衡的情况也分三种:1.1删除的节点度为0,且为红色,无需调整。
        //                          1.2删除的节点度为0,且为黑色,需调整。
        //                          2
        //                          .删除的节点度为1,将子节点涂为黑色即可。(只有一个子节点时,删除节点比为是黑色,其子节点为红色)
        //                          3
        //                          .删除的节点度为2,实际上删除节点知识替换了值,真正被删除的是其左子树中最大节点,而这个最大节点度为0,或1,所以又转向上面的情况。
        if (node.leftNode != null && node.rightNode != null) {
            //            度为2的节点,删除步骤为
            //            1.找到要删除节点的左子数中最大节点。
            //            2.替换待删除节点的值为这个最大节点值
            //            3.将这个最大节点删除
            RBNode<T> maxNode = node.leftNode;
            while (maxNode.rightNode != null) {
                maxNode = maxNode.rightNode;
            }
            remove(maxNode.val);
            node.val = maxNode.val;
        } else {
            //            度为1和度为2的节点一起处理,分两种情况。
            //            1.要删除的节点为根节点。
            //            2.要删除的节点为非根节点。
            RBNode<T> parent = parent(node);
            RBNode<T> brother = brother(node);
            RBNode<T> child =
                    node.leftNode == null ? node.rightNode : node.leftNode;
            //            根节点无父节点,直接将根节点指向子节点即可。
            if (parent == null) {
                root = child;
            } else {
                //                非根节点,找到它是父节点的左子节点,还是右子节点,然后通过修改父节点指针即可。
                adjustParent(node, child);
            }
            if (child == null) {
                //                删除节点度为0,且为黑色,需要调整。
                if (node.color.compareTo(Color.black) == 0) {
                    adjustDelete(parent, brother);
                }
            } else {
                //                删除节点度为1,只需将子节点涂黑。
                child.color = Color.black;
            }
        }
        size--;
        return true;
    }

    //    插入后调整红黑树。
    private void adjustInsert(RBNode<T> node) {
        //        处理情景4.1的情况,如果祖先节点是根节点,将根节点又设置回黑色。
        if (node == root) {
            node.color = Color.black;
            return;
        }
        RBNode<T> parent = parent(node);
        if (parent.color.compareTo(Color.black) == 0) {
            //            情景3:父节点为黑色
            return;
        } else {
            //            父节点如果为红色,那么必然存在祖先节点,因为根节点是黑色,而且两个红色节点不能相连。
            //            情景4:插入结点的父结点为红结点
            RBNode<T> grandparent = parent(parent);
            RBNode<T> uncle = uncle(node);
            if (uncle != null && uncle.color.compareTo(Color.red) == 0) {
                //                情景4.1:叔叔结点存在并且为红结点
                parent.color = Color.black;
                uncle.color = Color.black;
                grandparent.color = Color.red;
                adjustInsert(grandparent);
            } else {
                if (grandparent.leftNode == parent) {
                    //                    情景4
                    //                    .2:叔叔结点不存在或为黑结点,并且插入结点的父亲结点是祖父结点的左子结点
                    if (parent.leftNode == node) {
                        //                        情景4.2.1:插入结点是其父结点的左子结点
                        RTurn(grandparent);
                        grandparent.color = Color.red;
                        parent.color = Color.black;
                    } else {
                        //                        情景4.2.2:插入结点是其父结点的右子结点
                        LTurn(parent);
                        RTurn(grandparent);
                        node.color = Color.black;
                        grandparent.color = Color.red;
                    }
                } else {
                    //                    情景4
                    //                    .3:叔叔结点不存在或为黑结点,并且插入结点的父亲结点是祖父结点的右子结点
                    if (parent.leftNode == node) {
                        //                        情景4.3.1:插入结点是其父结点的左子结点
                        RTurn(parent);
                        LTurn(grandparent);
                        node.color = Color.black;
                        grandparent.color = Color.red;
                    } else {
                        //                        情景4.3.2:插入结点是其父结点的右子结点
                        LTurn(grandparent);
                        grandparent.color = Color.red;
                        parent.color = Color.black;
                    }
                }
            }
        }
    }

    //    删除后调整红黑树,brother无需进行null判断,因为当删除节点是度为0的黑色时,其左子树黑色点已经达到2(null算黑色),
    //    而兄弟节点如果为null,那么右子树黑色节点只有1,这是不可能的,在删除前,树一定是平衡的。
    //    而兄弟节点的子节点,只要是red,必然不为null,而只有是red的情况,我们才调用到,所以也不会出现空指针。
    //    像下面的兄弟节点的子节点为黑时,是不会有对这个黑色子节点的操作的,有些旋转后是它另外一个红色子节点上位了,
    //    而原来的兄弟节点下去当子节点了,依然不会涉及黑色子节点的操作。
    private void adjustDelete(RBNode<T> parent, RBNode<T> brother) {
        //        1.n为根节点,无需平衡操作
        if (parent == null) {
            return;
        }
        if (brother.color.compareTo(Color.black) == 0) {
            //            2.兄弟节点为黑色
            if (!(brother.leftNode != null &&
                    brother.leftNode.color.compareTo(Color.red) == 0) &&
                    !(brother.rightNode != null &&
                            brother.rightNode.color.compareTo(Color.red) ==
                                    0)) {
                //                2.1兄弟子节点全黑
                if (parent.color.compareTo(Color.red) == 0) {
                    //                    2.1.1父红
                    exchangeColor(parent, brother);
                } else {
                    //                    2.1.2父黑
                    brother.color = Color.red;
                    adjustDelete(parent(parent), brother(parent));
                }
            } else {
                //                2.2.1兄在左
                if (parent.leftNode == brother) {
                    //                    兄左子红
                    if (brother.leftNode != null &&
                            brother.leftNode.color.compareTo(Color.red) == 0) {
                        RTurn(parent);
                        exchangeColor(parent, brother);
                        brother.leftNode.color = Color.black;
                    } else {
                        //                        兄左子黑
                        RBNode<T> rightNode = brother.rightNode;
                        LTurn(brother);
                        exchangeColor(brother, rightNode);
                        adjustDelete(parent, rightNode);
                    }
                } else {
                    //                    2.2.2兄在右
                    if (brother.rightNode != null &&
                            brother.rightNode.color.compareTo(Color.red) == 0) {
                        //                        兄右子红
                        LTurn(parent);
                        exchangeColor(parent, brother);
                        brother.rightNode.color = Color.black;
                    } else {
                        //                        兄右子黑
                        RBNode<T> leftNode = brother.leftNode;
                        RTurn(brother);
                        exchangeColor(brother, leftNode);
                        adjustDelete(parent, leftNode);
                    }
                }
            }
        } else {
            //            3.兄弟节点为红色
            if (parent.leftNode == brother) {
                //                兄在左
                RBNode<T> rightNode = brother.rightNode;
                RTurn(parent);
                exchangeColor(parent, brother);
                adjustDelete(parent, rightNode);
            } else {
                //                兄在右
                RBNode<T> leftNode = brother.leftNode;
                LTurn(parent);
                exchangeColor(parent, brother);
                adjustDelete(parent, leftNode);
            }
        }
    }

    //    修改父节点的左右子节点指针。
    private void adjustParent(RBNode<T> node, RBNode<T> child) {
        RBNode<T> parent = parent(node);
        if (parent.leftNode != null && parent.leftNode == node) {
            parent.leftNode = child;
        } else {
            parent.rightNode = child;
        }
    }

    //    交换颜色
    private void exchangeColor(RBNode<T> node1, RBNode<T> node2) {
        Color color = node1.color;
        node1.color = node2.color;
        node2.color = color;
    }

    //    查找节点
    private RBNode<T> find(T val) {
        if (root == null || val == null) {
            return null;
        }
        RBNode<T> node = root;
        while (node != null) {
            if (node.val.compareTo(val) == 0) {
                return node;
            } else if (node.val.compareTo(val) > 0) {
                node = node.leftNode;
            } else {
                node = node.rightNode;
            }
        }
        return null;
    }

    //    左旋
    private void LTurn(RBNode<T> node) {
        RBNode<T> parent = parent(node);
        RBNode<T> rightNode = node.rightNode;
        if (parent == null) {
            root = rightNode;
        } else {
            adjustParent(node, rightNode);
        }
        node.rightNode = rightNode.leftNode;
        rightNode.leftNode = node;
    }

    //    右旋
    private void RTurn(RBNode<T> node) {
        RBNode<T> parent = parent(node);
        RBNode<T> leftNode = node.leftNode;
        if (parent == null) {
            root = leftNode;
        } else {
            adjustParent(node, leftNode);
        }
        node.leftNode = leftNode.rightNode;
        leftNode.rightNode = node;
    }

    //    查找父节点。
    private RBNode<T> parent(RBNode<T> current) {
        if (root == null || current == null) {
            return null;
        }
        RBNode<T> node = root;
        while (node != null) {
            if (node.leftNode == current || node.rightNode == current) {
                return node;
            } else if (node.val.compareTo(current.val) > 0) {
                node = node.leftNode;
            } else {
                node = node.rightNode;
            }
        }
        return null;
    }

    //    查找叔叔节点
    private RBNode<T> uncle(RBNode<T> node) {
        RBNode<T> parent = parent(node);
        return brother(parent);
    }

    //    查找兄弟节点
    private RBNode<T> brother(RBNode<T> node) {
        RBNode<T> parent = parent(node);
        if(parent==null){
            return null;
        }
        if (parent.leftNode != null && parent.leftNode == node) {
            return parent.rightNode;
        } else {
            return parent.leftNode;
        }
    }

    private void LDR(RBNode<T> node) {
        if (node == null) {
            return;
        }
        LDR(node.leftNode);
        System.out.println(node.val+","+node.color);
        LDR(node.rightNode);
    }

    public void LDR() {
        LDR(root);
    }
}

//红黑树节点。
class RBNode<T extends Comparable> {
    Color color;
    T val;
    RBNode<T> leftNode;
    RBNode<T> rightNode;
}

//枚举类,定义红黑两种颜色。
enum Color {
    red, black
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值