zoukankan      html  css  js  c++  java
  • 深入浅出红黑树(Java TreeMap)

     实现原理:红黑树
    什么是红黑树(数据来自百度百科):
        红黑树(自平衡二叉树)是每个节点都带有颜色属性的二叉查找树,颜色或红色或黑色。在二叉查找树强制一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求:
    性质1. 节点是红色或黑色。
    性质2. 根节点是黑色。
    性质3. 每个叶节点(NIL节点,空节点)是黑色的。
    性质4. 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
    性质5. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
        这些约束强制了红黑树的关键性质: 从根到叶子的最长的可能路径不多于最短的可能路径的两倍长。结果是这个树大致上是平衡的。因为操作比如插入、删除和查找某个值的最坏情况时间都要求与树的高度成比例,这个在高度上的理论上限允许红黑树在最坏情况下都是高效的,而不同于普通的二叉查找树。
     
    思路:
    1.创建空比较器,空Entry引用,entry个数默认为0
     //比较器,因为TreeMap是有序的,通过comparator接口我们可以对TreeMap的内部排序进行精密的控制
     private final Comparator<? super K> comparator;
     //TreeMap红-黑节点,为TreeMap的内部类
     private transient Entry<K,V> root = null;
        
     private transient int size = 0;
     
     private static final boolean RED   = false;
     
     private static final boolean BLACK = true;

    2.四个构造器,全部使用比较器,比较器基本容器/单位?

     public TreeMap() {
        comparator = null;
     }
     public TreeMap(Comparator<? super K> comparator) {
            this.comparator = comparator;
     }
     public TreeMap(Map<? extends K, ? extends V> m) {
            comparator = null;
            putAll(m);
     }
    public TreeMap(SortedMap<K, ? extends V> m) {
            comparator = m.comparator();
            try {
                buildFromSorted(m.size(), m.entrySet().iterator(), null, null);
            } catch (java.io.IOException cannotHappen) {
            } catch (ClassNotFoundException cannotHappen) {
            }
    }
     3.Entry类
    static final class Entry<K,V> implements Map.Entry<K,V> {
            K key;
            V value;
            Entry<K,V> left;
            Entry<K,V> right;
            Entry<K,V> parent;
            boolean color = BLACK;
        
            /**
             * Make a new cell with given key, value, and parent, and with
             * {@code null} child links, and BLACK color.
             */
            Entry(K key, V value, Entry<K,V> parent) {
                this.key = key;
                this.value = value;
                this.parent = parent;
            }
    
        }
    4.put方法
        两个点:
            构建一个有序二叉树
            平衡二叉树
     
    1、以根节点为初始节点进行检索。
    2、与当前节点进行比对,若新增节点值较大,则以当前节点的右子节点作为新的当前节点。
       否则以当前节点的左子节点作为新的当前节点。
    3、循环递归2步骤知道检索出合适的叶子节点为止。
    4、将新增节点与3步骤中找到的节点进行比对,如果新增节点较大,则添加为右子节点;否则添加为左子节点。
     
        1.root为空 
                新增根节点  
                return
        2.root不为空,比较器不为空,
                在root左右插入节点后 继续
                或者替换root值后     return
        3.root不为空,比较器为空
                key为空,抛出空指针
                在root左右插入节点后 继续
                或者替换root值后     return
    public V put(K key, V value) {
            //用t表示二叉树的当前节点 
            Entry<K,V> t = root;
            //t为null表示一个空树,即TreeMap中没有任何元素,直接插入
            if (t == null) {
                //比较key值,个人觉得这句代码没有任何意义,空树还需要比较、排序?
                compare(key, key); // type (and possibly null) check
                //将新的key-value键值对创建为一个Entry节点,并将该节点赋予给root
                root = new Entry<>(key, value, null);
                //容器的size = 1,表示TreeMap集合中存在一个元素 
                size = 1;
                modCount++;
                return null;
            }
            //cmp表示key排序的返回结果
            int cmp;
            //父节点 
            Entry<K,V> parent;
            // split comparator and comparable paths
          //指定的排序算法 
            Comparator<? super K> cpr = comparator;
            //非空比较器时
            ////如果cpr不为空,则采用既定的排序算法进行创建TreeMap集合
            if (cpr != null) {
                do {
                    //parent指向上次循环后的t
                    parent = t;
                    //比较新增节点的key和当前节点key的大小
                    cmp = cpr.compare(key, t.key);
                    //cmp返回值小于0,表示新增节点的key小于当前节点的key,
                    //则以当前节点的左子节点作为新的当前节点
                    if (cmp < 0)
                        t = t.left;
                    //cmp返回值大于0,表示新增节点的key大于当前节点的key,
                    //则以当前节点的右子节点作为新的当前节点
                    else if (cmp > 0)
                        t = t.right;
                    else //cmp返回值等于0,表示两个key值相等,则新值覆盖旧值,并返回新值
                        return t.setValue(value);
                } while (t != null);
            }
            else { //如果cpr为空,则采用默认的排序算法进行创建TreeMap集合
                //key不能为空
                if (key == null)
                    throw new NullPointerException();
                /* 下面处理过程和上面一样 */
                @SuppressWarnings("unchecked")
                    Comparable<? super K> k = (Comparable<? super K>) key;
                do {
                    parent = t;
                    cmp = k.compareTo(t.key);
                    if (cmp < 0)
                        t = t.left;
                    else if (cmp > 0)
                        t = t.right;
                    else
                        return t.setValue(value);
                } while (t != null);
            }
            //将新增节点当做parent的子节点
            Entry<K,V> e = new Entry<>(key, value, parent);
           //如果新增节点的key小于parent的key,则当做左子节点  
            if (cmp < 0)
                parent.left = e;
            else    //如果新增节点的key大于parent的key,则当做右子节点
                parent.right = e;
        
             /**
             *  上面已经完成了排序二叉树的的构建,将新增节点插入该树中的合适位置             
             *  下面fixAfterInsertion()方法就是对这棵树进行调整、平衡,具体过程参考上面的五种情况                  */ 
            fixAfterInsertion(e);
            size++;
            modCount++;
            return null;
        }
     上面的do{}代码仅仅是排序二叉树的核心算法,真正的实现红黑树依靠的是fixAfterInsertion(e)方法 
        红黑树是一棵平衡排序二叉树,普通的排序二叉树可能会出现失衡的情况,而fixAfterInsertion就是调      整结构实现红黑树 : 左旋右旋着色三个基本操作 
    /** From CLR */
        private void fixAfterInsertion(Entry<K,V> x) {
            //新节点着色
            x.color = RED;
            //循环到新节点 不为空且不是根且根的节点不是红色
            while (x != null && x != root && x.parent.color == RED) {
             
                //如果X的父节点(假设为:P)是其父节点的父节点(假设为:G)的左节点
                if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
                    //获取父节点的父节点的右节点y
                    Entry<K,V> y = rightOf(parentOf(parentOf(x)));
           //         G
           //       /   
           //     p       y
           //    / 
           //   x       
                    if (colorOf(y) == RED) {
                        setColor(parentOf(x), BLACK);
                        setColor(y, BLACK);
                        setColor(parentOf(parentOf(x)), RED);
                        // ?????
                        x = parentOf(parentOf(x));
                    } else {
                        //右旋
                        if (x == rightOf(parentOf(x))) {
                            x = parentOf(x);
                            //以X的父节点的父节点(G)为中心左旋转
                            rotateLeft(x);
                        }
                        setColor(parentOf(x), BLACK);
                        setColor(parentOf(parentOf(x)), RED);
                        rotateRight(parentOf(parentOf(x)));
                    }
                } else {
                    //如果X的父节点(假设为:P)是其父节点的父节点(假设为:G)的右节点
                    //                G
                    //              /   
                    //             y     P
                    //                    
                    //                     x
                    //
                    
                    Entry<K,V> y = leftOf(parentOf(parentOf(x)));
                    if (colorOf(y) == RED) {
                        setColor(parentOf(x), BLACK);
                        setColor(y, BLACK);
                        setColor(parentOf(parentOf(x)), RED);
                        x = parentOf(parentOf(x));
                    } else {
                        //左旋
                        if (x == leftOf(parentOf(x))) {
                            x = parentOf(x);
                            //以X的父节点的父节点(G)为中心右旋转
                            rotateRight(x);
                        }
                        setColor(parentOf(x), BLACK);
                        setColor(parentOf(parentOf(x)), RED);
                        rotateLeft(parentOf(parentOf(x)));
                    }
                }
            }
            root.color = BLACK;
        }

    右旋方法(左旋类似)

    /** From CLR */
        private void rotateRight(Entry<K,V> p) {
            if (p != null) {
                //    p
                //   /
                //   L
                //将L设置为P的左子树
                Entry<K,V> l = p.left;
                //将L的右子树设置为P的左子树
                p.left = l.right;
                 //若L的右子树不为空,则将P设置L的右子树的父节点
                if (l.right != null) l.right.parent = p;
                //将P的父节点设置为L的父节点
                l.parent = p.parent;
                 //如果P的父节点为空,则将L设置根节点 
                if (p.parent == null)
                    root = l;
                //若P为其父节点的右子树,则将L设置为P的父节点的右子树
                else if (p.parent.right == p)
                    p.parent.right = l;
                //否则将L设置为P的父节点的左子树
                else p.parent.left = l;
                //将P设置为L的右子树
                l.right = p;
                //将L设置为P的父节点
                p.parent = l;
            }
        }
    欢迎关注我的公众号:老张大魔王 >>> 不定时更新哦
  • 相关阅读:
    Struts2+Spring3+Mybatis3开发环境搭建
    spring+struts2+mybatis
    【LeetCode】Populating Next Right Pointers in Each Node
    【LeetCode】Remove Duplicates from Sorted Array
    【LeetCode】Remove Duplicates from Sorted Array II
    【LeetCode】Binary Tree Inorder Traversal
    【LeetCode】Merge Two Sorted Lists
    【LeetCode】Reverse Integer
    【LeetCode】Same Tree
    【LeetCode】Maximum Depth of Binary Tree
  • 原文地址:https://www.cnblogs.com/dougest/p/8735083.html
Copyright © 2011-2022 走看看