不是吧,4000多页合集的计算机、网络、算法知识总结

  1. 每个节点要么是黑色,要么是红色。

  2. 根节点是黑色。

  3. 每个叶子节点(NIL)是黑色。

  4. 每个红色结点的两个子结点一定都是黑色。

  5. 任意一结点到每个叶子结点的路径都包含数量相同的黑结点。

红黑树能自平衡,它靠的是什么?三种操作:左旋、右旋和变色

1 旋转操作


1.1 概念讲解

左旋:以某个节点作为旋转点,其右子节点变为旋转节点的父节点,右子节点的左子节点变为旋转节点的右子节点,左子节点保持不变。

å¨è¿éæå¥å¾çæè¿°

å¨è¿éæå¥å¾çæè¿°

右旋:以某!个节点作为旋转点,其左子节点变为旋转节点的父节点,左子节点的右子节点变为旋转节点的左子节点,右子节点保持不变。

å¨è¿éæå¥å¾çæè¿°

å¨è¿éæå¥å¾çæè¿°

1.2 代码实现

先进行类结构定义


package com.bobo.util.treemap;



public class BRTree {



    private static final boolean RED = false;

    private static final boolean BLACK = true;



    private RBNode root;



    public RBNode getRoot() {

        return root;

    }



    public void setRoot(RBNode root) {

        this.root = root;

    }





    /**

     * 表示 节点

     * @param <K>

     * @param <V>

     */

    static class RBNode<K extends Comparable<K>,V>{

        // 节点是双向的

        private RBNode parent;

        private RBNode left;

        private RBNode right;

        private boolean color;

        private K key;

        private V value;



        public RBNode() {

        }



        public RBNode(RBNode parent, RBNode left, RBNode right, boolean color, K key, V value) {

            this.parent = parent;

            this.left = left;

            this.right = right;

            this.color = color;

            this.key = key;

            this.value = value;

        }



        public RBNode getParent() {

            return parent;

        }



        public void setParent(RBNode parent) {

            this.parent = parent;

        }



        public RBNode getLeft() {

            return left;

        }



        public void setLeft(RBNode left) {

            this.left = left;

        }



        public RBNode getRight() {

            return right;

        }



        public void setRight(RBNode right) {

            this.right = right;

        }



        public boolean isColor() {

            return color;

        }



        public void setColor(boolean color) {

            this.color = color;

        }



        public K getKey() {

            return key;

        }



        public void setKey(K key) {

            this.key = key;

        }



        public V getValue() {

            return value;

        }



        public void setValue(V value) {

            this.value = value;

        }

    }

}

复制代码

左旋代码实现


 /**

     * 围绕p左旋

     *     p               pr(r)

     *    / |             / \

     *   pl  pr(r) =>    p   rr

     *      / \         / \

     *     rl  rr     pl  rl

     *

     *  左旋的时候

     *  p-pl 和 pr-rr的关系不变

     *  pr-rl 要变为 p-rl

     *      也就是 rl要变为 p的右子节点

     *      同时 p要成为 rl 的父节点

     *  还有就是要判断  p 是否有父节点

     *  如果没有

     *     r 变为 root 节点

     *  如果有

     *     r.parent = p.parent

     *     还要设置 r为 p.parent 的子节点(可能左也可能右)

     *     如果 p.parent.left == p

     *        p.parent.left = r;

     *     否则

     *        p.parent.right = r;

     *    最后

     *       p.parent = r;

     *       r.left = p;

     * @param p

     */

    private void leftRotate(RBNode p){

        if(p != null){

            RBNode r = p.right;

            // 1.设置 pr-rl 要变为 p-rl

            // 把rl设置到p的右子节点

            p.right = r.left;

            if(r.left != null){

                // 设置rl的父节点为p

                r.left.parent = p;

            }

            // 2.判断p的父节点情况

            r.parent = p.parent; // 不管 p是否有父节点,都把这个父节点设置为 r的父节点

            if(p.parent == null){

                root = r; // p没有父节点 则r为root节点

            }else if(p.parent.left == p){

                p.parent.left = r; // 如果p为 p.parent的左子节点 则 r 也为 p.parent的左子节点

            }else{

                p.parent.right = r; // 反之设置 r 为 p.parent的右子节点

            }

            // 最后 设置 p 为 r 的左子节点

            r.left = p;

            p.parent = r;

        }

    }

复制代码

右旋实现:


 /**

     * 围绕p右旋

     * @param p

     */

    public void rightRotate(RBNode p){

        if(p != null){

            RBNode r = p.left;

            p.left = r.right;

            if(r.right != null){

                r.right.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.right = p;

            p.parent = r;

        }

    }

复制代码

2 新增节点


2-3-4树中结点添加需要遵守以下规则:

  • 插入都是向最下面一层插入

  • 升元:将插入结点由 2-结点升级成 3-结点,或由 3-结点升级成 4-结点;

  • 向 4-结点插入元素后,需要将中间元素提到父结点升元,原结点变成两个 2-结点,再把元素插入2-结点中,如果父结点也是 4-结点,则递归向上层升元,至到根结点后将树高加1;

而将这些规则对应到红黑树里,就是:

  • 新插入的结点颜色为 红色 ,这样才可能不会对红黑树的高度产生影响。

  • 2-结点对应红黑树中的单个黑色结点,插入时直接成功(对应 2-结点升元)。

  • 3-结点对应红黑树中的 黑+红 子树,插入后将其修复成 红+黑+红 子树(对应 3-结点升元);

  • 4-结点对应红黑树中的 红+黑+红 子树,插入后将其修复成 红色祖父+黑色父叔+红色孩子 子树,然后再把祖父结点当成新插入的红色结点递归向上层修复,直至修复成功或遇到 root 结点;

公式:红黑树+新增一个节点(红色)**=**对等的2-3-4树+新增一个节点

2.1 新增节点示例

我们通过新增2-3-4树的过程来映射对应的红黑树的节点新增

å¨è¿éæå¥å¾çæè¿°

2-3-4树的新增(全部在叶子节点完成)

1.新增一个节点,2 节点

å¨è¿éæå¥å¾çæè¿°

2.新增一个节点,与2节点合并,直接合并

å¨è¿éæå¥å¾çæè¿°

3.新增一个节点,与3节点合并,直接合并

å¨è¿éæå¥å¾çæè¿°

插入的值的位置会有3种情况

对应的红黑树为:

å¨è¿éæå¥å¾çæè¿°

4.新增一个节点,与4节点合并,此时需要分裂、

å¨è¿éæå¥å¾çæè¿°

插入值的位置可能是

å¨è¿éæå¥å¾çæè¿°

对应的红黑树的结构为

å¨è¿éæå¥å¾çæè¿°

2.2 新增代码实现

红黑树的新增规则我们理清楚了,接下来就可以通过Java代码来具体的实现了。

先实现插入节点,这就是一个普通的二叉树的插入


 /**

     * 新增节点

     * @param key

     * @param value

     */

    public void put(K key , V value){

        RBNode t = this.root;

        if(t == null){

            // 说明之前没有元素,现在插入的元素是第一个

            root = new RBNode<>(key , value == null ? key : value,null);

            return ;

        }

        int cmp ;

        // 寻找插入位置

        // 定义一个双亲指针

        RBNode parent;

        if(key == null){

            throw new NullPointerException();

        }

        // 沿着跟节点找插入位置

        do{


### 最后

**如果觉得本文对你有帮助的话,不妨给我点个赞,关注一下吧!**

**[资料免费领取方式:点击这里](https://gitee.com/vip204888/java-p7)**

![](https://img-blog.csdnimg.cn/img_convert/daf8cfc84fdb5227b0b1618c391814ae.png)

id put(K key , V value){

        RBNode t = this.root;

        if(t == null){

            // 说明之前没有元素,现在插入的元素是第一个

            root = new RBNode<>(key , value == null ? key : value,null);

            return ;

        }

        int cmp ;

        // 寻找插入位置

        // 定义一个双亲指针

        RBNode parent;

        if(key == null){

            throw new NullPointerException();

        }

        // 沿着跟节点找插入位置

        do{


### 最后

**如果觉得本文对你有帮助的话,不妨给我点个赞,关注一下吧!**

**[资料免费领取方式:点击这里](https://gitee.com/vip204888/java-p7)**

[外链图片转存中...(img-uOQgvt2D-1628432562796)]

![](https://img-blog.csdnimg.cn/img_convert/6e6463247e74e06477dd5eb773261f4b.png)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值