精华内容
下载资源
问答
  • TreeMap的实现原理

    千次阅读 2018-09-17 20:48:08
    TreeMap的原理(jdk1.7) TreeMap的底层实现基于二叉树的红黑树,插入的值是按一定顺序排序的。 TreeMap的构造方法 TreeMap有四个构造方法  1  public TreeMap() {  comparator = null;  }  创键空的...

    TreeMap的原理(jdk1.7)

    TreeMap的底层实现基于二叉树的红黑树,插入的值是按一定顺序排序的。

    TreeMap的构造方法

    TreeMap有四个构造方法

        1
        public TreeMap() {
            comparator = null;
        }
        创键空的TreeMap,采用自然排序
        
        2
        public TreeMap(Comparator<? super K> comparator) {
            this.comparator = comparator;
        }
        创建一个空的TreeMap,排序是根据comparator排序的,
        
        3
        public TreeMap(Map<? extends K, ? extends V> m) {
            comparator = null;
            putAll(m);
        }
        创建一个新的TreeMap,m集合的值全部复制到TreeMap上,排序是用自然排序。运行时间的在n*log(n)时间内运行。
        
        4
        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) {
            }
        }
        创建一个新的TreeMap,并键m的值全部复制到TreeMap上,排序用的是m中指定的排序。这个方法运行时间是在线性时间内。

    TreeMap的put()方法

    public V put(K key, V value) {
            Entry<K,V> t = root;
            /*如果此时的红黑树为null,即这是插入的第一个节点,直接设置为根节点*/
            if (t == null) {
                /*类型检查,不能为null*/
                compare(key, key); 
    
                root = new Entry<>(key, value, null);
                size = 1;
                modCount++;
                return null;
            }
            int cmp;
            Entry<K,V> parent;
            
            Comparator<? super K> cpr = comparator;
    /*判断该TreeMap在创建时是否制定了Comparator,若是,排序使用Comparatr中的compare()方法,没有则使用默认的Comparable的自然排序*/
            if (cpr != null) {
                /*循环红黑书,能匹配到key就替换value*/
                do {
                    parent = t;
                    cmp = cpr.compare(key, t.key);
                    if (cmp < 0)
                        t = t.left;
                    else if (cmp > 0)
                        t = t.right;
                    else
                        return t.setValue(value);
                } while (t != null);
            }
            else {
                if (key == null)
                    throw new NullPointerException();
                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);
            }
            Entry<K,V> e = new Entry<>(key, value, parent);
            if (cmp < 0)
                parent.left = e;
            else
                parent.right = e;
    //添加完节点,开始维护红黑树
            fixAfterInsertion(e);
            size++;
            modCount++;
            return null;
        }

    因为是根据key排序的,所以要求TreeMap中的key数据类型要一致,不然会报ClassCastException。

    Comparable("自然排序")

    在java中,有一个接口类Comparable。该类有一个方法compareTo(T o)。所有的封装基本类型都实现了该接口并重写了这个方法。compareTo()就是我们常说的自然排序。注意:key不能为null,因为会调用key.compareTo()方法。

    例子:

    Map treeMap = new TreeMap();

    Comparator(比较器)

    TreeMap中使用的比较器。若我们不想用自然排序设计的比较规则,想自己设计一套规则,则需要创建该接口的示例,并实现compare()方法,在创建TreeMap时通过构造方法传入。注意:key可以为null,如果你想限制你可以在Comparator的compare()方法里面去限制。

    例子:

    Comparator comparator = new Comparator() {
                @Override
                public int compare(Object o1, Object o2) {
                    if(o1.toString().length()>o2.toString().length()){
                        return 1;
                    }else{
                        return -1;
                    }
                }
            };
            Map treeMap = new TreeMap(comparator);

    TreeMap的get()方法

    public V get(Object key) {
            Entry<K,V> p = getEntry(key);
            return (p==null ? null : p.value);
    }
    
    final Entry<K,V> getEntry(Object key) {
            // 区别是自然排序还是Comparator排序
            if (comparator != null)
                return getEntryUsingComparator(key);
            if (key == null)
                throw new NullPointerException();
            Comparable<? super K> k = (Comparable<? super K>) key;
            Entry<K,V> p = root;
            while (p != null) {
                int cmp = k.compareTo(p.key);
                if (cmp < 0)
                    p = p.left;
                else if (cmp > 0)
                    p = p.right;
                else
                    return p;
            }
            return null;
        }

    TreeMap的get()倒是没什么奇特之处,只是要区别是自然排序和Comparator排序,根据不同的排序调用不同的比较方法去定位想要的数据。因为底层是红黑树,所以检索的速度相当快,时间复杂度为O(log(n))。

    例子

    用默认自然排序:

            Map treeMap = new TreeMap();
            treeMap.put("1","1");
            treeMap.put("2","2");
            treeMap.put("7","7");
            treeMap.put("3","3");
            treeMap.put("8","8");
            treeMap.put("4","4");
            treeMap.put("5","5");
            Set keySet = treeMap.keySet();
            Iterator keyItr = keySet.iterator();
            while(keyItr.hasNext()){
                String key = (String)keyItr.next();
                String value = (String)treeMap.get(key);
                System.out.println(value);
            }
    	输出结果:
    		1
    		2
    		3
    		4
    		5
    		7
    		8

    使用比较器comparator

            public void comparatorTest(){
    			class DemoComparator implements Comparator<String>{
    				@Override
    				public int compare(String o1, String o2) {
    					if(o1.length()==o2.length() && o1.equals(o2)){
    						return 0;
    					}else if(o1.length()<o2.length()){
    						return 1;
    					}else{
    						return -1;
    					}
    				}
    			}
    			Map treeMap = new TreeMap(new DemoComparator());
    			treeMap.put("111","1");
    			treeMap.put("2","2");
    			treeMap.put("71","7");
    			treeMap.put("31111","3");
    			treeMap.put("8111","8");
    			treeMap.put("4111111","4");
    			treeMap.put("511111","5");
    			Set keySet = treeMap.keySet();
    			Iterator keyItr = keySet.iterator();
    			while(keyItr.hasNext()){
    				String key = (String)keyItr.next();
    				System.out.println("key:"+key);
    				String value = (String)treeMap.get(key);
    				System.out.println("value:"+value);
    			}
    		}
    		结果:
    			key:4111111,value:4
    			key:511111,value:5
    			key:31111,value:3
    			key:8111,value:8
    			key:111,value:1
    			key:71,value:7
    			key:2,value:2
    		简单的设计一个根据key字符串长度来排序的比较器。

     

     

     

     

     

     

     

     

     

     

     

    展开全文
  • 转自 ...1、概述 文章内容基于JDK1.7进行分析,之所以选用这个版本,是因为1.8有些类做了改动,增加...TreeMap实现了SortedMap接口,它是有序集合。而且是一个红黑树结构,每个key-value都作为一个红黑树节点。...

    转自
    https://my.oschina.net/90888/blog/1626065

    1、概述

    文章的内容基于JDK1.7进行分析,之所以选用这个版本,是因为1.8的有些类做了改动,增加了阅读的难度,虽然是1.7,但是对于1.8做了重大改动的内容,文章也会进行说明。

    TreeMap实现了SortedMap接口,它是有序的集合。而且是一个红黑树结构,每个key-value都作为一个红黑树的节点。如果在调用TreeMap的构造函数时没有指定比较器,则根据key执行自然排序。这点会在接下来的代码中做说明,如果指定了比较器则按照比较器来进行排序。

    2、数据结构

    继承关系

    public class TreeMap<K,V>
        extends AbstractMap<K,V>
        implements NavigableMap<K,V>, Cloneable, java.io.Serializable {}
    

    实现接口

    Serializable, Cloneable, Map<K,V>, NavigableMap<K,V>, SortedMap<K,V> 
    

    基本属性

    private final Comparator<? super K> comparator;  //比较器,是自然排序,还是定制排序 ,使用final修饰,表明一旦赋值便不允许改变
    private transient Entry<K,V> root = null;  //红黑树的根节点
    private transient int size = 0;     //TreeMap中存放的键值对的数量
    private transient int modCount = 0;   //修改的次数
    

    源码解析

    由于TreeMap中源码较长,接下来将分段解析部分源码。既然是红黑树存储,肯定要有数据结构(Node)节点的。看一下TreeMap中关于节点的定义部分。

    static final class Entry<K,V> implements Map.Entry<K,V> {
        K key;    //键
        V value;    //值
        Entry<K,V> left = null;     //左孩子节点
        Entry<K,V> right = null;    //右孩子节点
        Entry<K,V> parent;          //父节点
        boolean color = BLACK;      //节点的颜色,在红黑树种,只有两种颜色,红色和黑色
    
        //构造方法,用指定的key,value ,parent初始化,color默认为黑色
        Entry(K key, V value, Entry<K,V> parent) {
            this.key = key;
            this.value = value;
            this.parent = parent;
        }
    
        //返回key
        public K getKey() {
            return key;
        }
    
        //返回该节点对应的value
        public V getValue() {
            return value;
        }
    
        //替换节点的值,并返回旧值
        public V setValue(V value) {
            V oldValue = this.value;
            this.value = value;
            return oldValue;
        }
        //重写equals()方法
        public boolean equals(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry<?,?> e = (Map.Entry<?,?>)o;
            //两个节点的key相等,value相等,这两个节点才相等
            return valEquals(key,e.getKey()) && valEquals(value,e.getValue());
        }
        //重写hashCode()方法
        public int hashCode() {
            int keyHash = (key==null ? 0 : key.hashCode());
            int valueHash = (value==null ? 0 : value.hashCode());
            //key和vale hash值得异或运算,相同则为零,不同则为1 
            return keyHash ^ valueHash;
        }
        //重写toString()方法
        public String toString() {
            return key + "=" + value;
        }
    }
    

    构造方法

    //构造方法,comparator用键的顺序做比较
    public TreeMap() {
        comparator = null;
    }
    
    //构造方法,提供比较器,用指定比较器排序
    public TreeMap(Comparator<? super K> comparator) {
        his.comparator = comparator;
    }
    
    //将m中的元素转化daoTreeMap中,按照键的顺序做比较排序
    public TreeMap(Map<? extends K, ? extends V> m) {
        comparator = null;
        putAll(m);
    }
    
    //构造方法,指定的参数为SortedMap
    //采用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) {
        }
    }
    

    TreeMap提供了四个构造方法,实现了方法的重载。无参构造方法中比较器的值为null,采用自然排序的方法,如果指定了比较器则称之为定制排序.

    • 自然排序:TreeMap的所有key必须实现Comparable接口,所有的key都是同一个类的对象

    • 定制排序:创建TreeMap对象传入了一个Comparator对象,该对象负责对TreeMap中所有的key进行排序,采用定制排序不要求Map的key实现Comparable接口。等下面分析到比较方法的时候在分析这两种比较有何不同。

    对于Map来说,使用的最多的就是**put()/get()/remove()**等方法,下面依次进行分析

    put()

    public V put(K key, V value) {
        Entry<K,V> t = root;     //红黑树的根节点
        if (t == null) {        //红黑树是否为空
            compare(key, key); // type (and possibly null) check
            //构造根节点,因为根节点没有父节点,传入null值。 
            root = new Entry<>(key, value, null);  
            size = 1;     //size值加1
            modCount++;    //改变修改的次数
            return null;    //返回null 
        }
        int cmp;
        Entry<K,V> parent;    //定义节点
        // split comparator and comparable paths
        Comparator<? super K> cpr = comparator;     //获取比较器
        if (cpr != null) {      //如果定义了比较器,采用自定义比较器进行比较
            do {
                parent = t;      //将红黑树根节点赋值给parent
                cmp = cpr.compare(key, t.key);     //比较key, 与根节点的大小
                if (cmp < 0)      //如果key < t.key , 指向左子树
                    t = t.left;   //t = t.left  , t == 它的做孩子节点
                else if (cmp > 0)
                    t = t.right;  //如果key > t.key , 指向它的右孩子节点
                else
                    return t.setValue(value);      //如果它们相等,替换key的值
            } while (t != null);        //循环遍历
        }
        else {
            //自然排序方式,没有指定比较器
            if (key == null)
                throw new NullPointerException();  //抛出异常
            Comparable<? super K> k = (Comparable<? super K>) key;    //类型转换
            do {
                parent = t;
                cmp = k.compareTo(t.key);
                if (cmp < 0)     // key < t.key 
                    t = t.left;   //左孩子
                else if (cmp > 0)   // key > t.key 
                    t = t.right;    //右孩子
                else
                    return t.setValue(value);   //t == t.key , 替换value值
            } while (t != null);
        }
        Entry<K,V> e = new Entry<>(key, value, parent);   //创建新节点,并制定父节点
        //根据比较结果,决定新节点为父节点的左孩子或者右孩子
        if (cmp < 0)
            parent.left = e;
        else
            parent.right = e;
        fixAfterInsertion(e);   //新插入节点后重新调整红黑树 
        size++;
        modCount++;
        return null;
    }
    //比较方法,如果comparator==null ,采用comparable.compartTo进行比较,否则采用指定比较器比较大小
    final int compare(Object k1, Object k2) {
        return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2)
            : comparator.compare((K)k1, (K)k2);
    }
    
    private void fixAfterInsertion(Entry<K,V> x) {
        //插入的节点默认的颜色为红色
        x.color = RED;    //
        //情形1: 新节点x 是树的根节点,没有父节点不需要任何操作
        //情形2: 新节点x 的父节点颜色是黑色的,也不需要任何操作
    
        while (x != null && x != root && x.parent.color == RED) {
        //情形3:新节点x的父节点颜色是红色的
        //判断x的节点的父节点位置,是否属于左孩子
        if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
              //获取x节点的父节点的兄弟节点,上面语句已经判断出x节点的父节点为左孩子,所以直接取右孩子
             Entry<K,V> y = rightOf(parentOf(parentOf(x)));
             //判断是否x节点的父节点的兄弟节点为红色。
             if (colorOf(y) == RED) {
                  setColor(parentOf(x), BLACK); // x节点的父节点设置为黑色
                  setColor(y, BLACK);           // y节点的颜色设置为黑色
                  setColor(parentOf(parentOf(x)), RED); // x.parent.parent设置为红色
                  x = parentOf(parentOf(x)); // x == x.parent.parent ,进行遍历。
             } else {
                   //x的父节点的兄弟节点是黑色或者缺少的
                   if (x == rightOf(parentOf(x))) {   //判断x节点是否为父节点的右孩子
                        x = parentOf(x);     //x == 父节点
                        rotateLeft(x);    //左旋转操作
                   }
                   //x节点是其父的左孩子
                   setColor(parentOf(x), BLACK);
                   setColor(parentOf(parentOf(x)), RED);  //上面两句将x.parent 和x.parent.parent的颜色做调换
                   rotateRight(parentOf(parentOf(x)));   //进行右旋转
                }
            } else {
                Entry<K,V> y = leftOf(parentOf(parentOf(x)));  //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 == leftOf(parentOf(x))) {     //x 是其父亲的左孩子
                        x = parentOf(x);
                        rotateRight(x);    //以父节点为旋转点,进行右旋操作
                    }
                    setColor(parentOf(x), BLACK);    //父节点为设置为黑色
                    setColor(parentOf(parentOf(x)), RED);  //祖父节点设置为红色
                    rotateLeft(parentOf(parentOf(x)));  //以父节点为旋转点,进行左旋操作
                }
            }
        }
        root.color = BLACK; //通过节点位置的调整,最终将红色的节点条调换到了根节点的位置,根节点重新设置为黑色
    }
    

    红黑树是一个更高效的检索二叉树,有如下特点:

    • 每个节点只能是红色或者黑色
    • 根节点永远是黑色的
    • 所有的叶子的子节点都是空节点,并且都是黑色的
    • 每个红色节点的两个子节点都是黑色的(不会有两个连续的红色节点)
    • 从任一个节点到其子树中每个叶子节点的路径都包含相同数量的黑色节点(叶子节点到根节点的黑色节点数量每条路径都相同)

    上面的代码,详细的标注了每条语句的作用,但是我相信,如果你没有一定的功力,即使注释已经很详细了,你也会是一脸懵逼 ,二脸懵逼,全脑懵逼中,下面配合图片来梳理一下代码所表示的含义:

    当一个默认为红色的节点插入树中,其实对应的是7种可能发生的情况,分别进行叙述:

    • 情形1新插入的节点是红黑树的根节点,没有父节点,无需任何的操作,直接将颜色设置为黑色就可以了

    • 情形2新节点的父节点颜色是黑色的,新插入的节点是红色的。也无需任何的操作。因为新节点的插入并没有影响到红黑书的特点

    • 情形3新节点的父节点(左孩子节点)颜色是红色的,而父节点的兄弟节点颜色也是红色的。那么情况就出现了,此时插入的节点就违反了红黑树的特点4 ,需要对红黑树进行调整。 操作看下图:
      在这里插入图片描述
      调整操作如上图,将父节点和父节点的兄弟节点,都修改为红色,然后将祖父节点修改为红色,因为修改了祖父节点的颜色,祖父节点可能会发生颜色的冲突,所以将新插入的节点修改为祖父节点,在进行调整。

    • 情形4父节点(左孩子节点)的颜色为红色,父节点的兄弟节点的颜色为黑色或者为null,新插入的节点为父节点的右孩子节点。如下图:
      在这里插入图片描述
      此时以父节点为旋转点,就新插入的节点进行左旋操作。就变成了情形5对应的情况,将执行情形5的操作

    • 情形5:父节点(左孩子节点)的颜色为红色,父节点的兄弟节点颜色为黑色或者null,新插入节点为父亲的左孩子节点。如下图:
      在这里插入图片描述

    • 情形6 和情形7的操作与情形4和情形5的操作相同,它们之前的区别是父节点为右孩子节点,再次不再赘述。

    remove()

    public V remove(Object key) {
        Entry<K,V> p = getEntry(key);  //根据key查找节点,并返回该节点
        if (p == null)
            return null;
    
        V oldValue = p.value;    //获取key对应的值
        deleteEntry(p);     //删除节点
        return oldValue;   //返回key对应的值
    }
    
    final Entry<K,V> getEntry(Object key) {
        //根据键寻找节点,有非为两种方式,如果定制了比较器,采用定制排序方式,否则使用自然排序
        if (comparator != null) 
            return getEntryUsingComparator(key); //循环遍历树,寻找和key相等的节点
        if (key == null)
            throw new NullPointerException();
        Comparable<? super K> k = (Comparable<? super K>) key;
        Entry<K,V> p = root;
        while (p != null) {  //循环遍历树,寻找和key相等的节点
            int cmp = k.compareTo(p.key);
            if (cmp < 0)
                p = p.left;
            else if (cmp > 0)
                p = p.right;
            else
                return p;
        }
        return null;
    }
    //删除节点
    private void deleteEntry(Entry<K,V> p) {
        modCount++;  //记录修改的次数
        size--;   //数量减1
    
        //当前节点的两个孩子都不为空
        if (p.left != null && p.right != null) {
            //寻找继承者,继承者为当前节点的右孩子节点或者右孩子节点的最小左孩子
            Entry<K,V> s = successor(p);
            p.key = s.key;     //key - value  的替换 ,并没有替换颜色
            p.value = s.value;
            p = s;  //指向继承者
        } // p has 2 children
    
        // Start fixup at replacement node, if it exists.
        //开始修复树结构,继承者的左孩子不为空,返回左孩子,否则返回右孩子
        //不可能存在左右两个孩子都存在的情况,successor寻找的就是最小节点,它的左孩子节点为null
        Entry<K,V> replacement = (p.left != null ? p.left : p.right);
    
        if (replacement != null) {
            // Link replacement to parent
            //已经被选为继承者,当前拥有的一切放弃,所以将孩子交给爷爷抚养
            replacement.parent = p.parent;
            //p节点没有父节点,则指向根节点
            if (p.parent == null)
               root = replacement;
            //如果p为左孩子,如果p为左孩子,则将p.parent.left = p.left
            else if (p == p.parent.left)
                p.parent.left  = replacement;
            else
                p.parent.right = replacement;
    
            //删除p节点到左右分支,和父节点的引用
            p.left = p.right = p.parent = null;
    
            // Fix replacement
            if (p.color == BLACK)
                //恢复颜色分配
                fixAfterDeletion(replacement);
        } else if (p.parent == null) { // return if we are the only node.
            //红黑书中父节点为空的只能是根节点。
            root = null;
        } else { //  No children. Use self as phantom replacement and unlink.
            if (p.color == BLACK)
                fixAfterDeletion(p);
    
            if (p.parent != null) {
                if (p == p.parent.left)
                    p.parent.left = null;
                else if (p == p.parent.right)
                    p.parent.right = null;
                p.parent = null;
            }
        }
    }
    private void fixAfterDeletion(Entry<K,V> x) {
        //不是根节点,颜色为黑色,调整结构
        while (x != root && colorOf(x) == BLACK) {
    
            //判断x是否为左孩子
            if (x == leftOf(parentOf(x))) {
                //x的兄弟节点
                Entry<K,V> sib = rightOf(parentOf(x));
                //若兄弟节点是红色
                if (colorOf(sib) == RED) {
                    setColor(sib, BLACK);   //设置兄弟节点变为黑色
                    setColor(parentOf(x), RED);  //父节点设置为红色
                    rotateLeft(parentOf(x));   //左旋父节点
                    sib = rightOf(parentOf(x)); //重新设置x的兄弟节点
                }
    
                if (colorOf(leftOf(sib))  == BLACK &&
                    colorOf(rightOf(sib)) == BLACK) {
                    setColor(sib, RED); //兄弟节点的两个孩子都是黑色的重新设置兄弟节点的颜色,修改为红色
                    x = parentOf(x);   //将x定位到父节点
                } else {
                    if (colorOf(rightOf(sib)) == BLACK) {   //兄弟节点的右孩子是黑色的,左孩子是红色的
                        setColor(leftOf(sib), BLACK);  //设置左孩子节点为黑色
                        setColor(sib, RED); //兄弟节点为红色
                        rotateRight(sib);   //右旋
                        sib = rightOf(parentOf(x));  //右旋后重新设置兄弟节点
                    }
                    setColor(sib, colorOf(parentOf(x)));  //兄弟节点颜色设置和父节点的颜色相同
                    setColor(parentOf(x), BLACK);   //父节点设置为黑色
                    setColor(rightOf(sib), BLACK);  //将兄弟节点的有孩子设置为黑色
                    rotateLeft(parentOf(x));   //左旋
                    x = root;  //设置x为根节点
                }
            } else { // symmetric
                //x为父节点的右节点,参考上面的操作
                Entry<K,V> sib = leftOf(parentOf(x));
    
                if (colorOf(sib) == RED) {
                    setColor(sib, BLACK);
                    setColor(parentOf(x), RED);
                    rotateRight(parentOf(x));
                    sib = leftOf(parentOf(x));
                }
    
                if (colorOf(rightOf(sib)) == BLACK &&colorOf(leftOf(sib)) == BLACK) {
                        setColor(sib, RED);
                        x = parentOf(x);
                } else {
                    if (colorOf(leftOf(sib)) == BLACK) {
                        setColor(rightOf(sib), BLACK);
                        setColor(sib, RED);
                        rotateLeft(sib);
                        sib = leftOf(parentOf(x));
                    }
                    setColor(sib, colorOf(parentOf(x)));
                    setColor(parentOf(x), BLACK);
                    setColor(leftOf(sib), BLACK);
                    rotateRight(parentOf(x));
                    x = root;
                }
            }
        }
    
        setColor(x, BLACK);
    }
    

    删除红黑树的操作比插入操作要稍微麻烦一点,分为两步:

    • 以排序二叉树的方法删除指定节点。删除的节点存在三种情况:

      • 被删除节点,没有左右孩子节点,直接删除即可
      • 被删除节点,有一个孩子节点,那么让它的孩子节点指向它的父节点即可
      • 被删除的节点,有两个非空的孩子节点,那么需要找到该节点的前驱或者后继节点,更换元素值,在将前驱或者后继节点删除(任意一个节点的前驱或者后继都必定至多有一个非空的子节点,可以按照前面的两种情形进行操作)
    • 进行颜色的调换和树的旋转,满足红黑树的特征

    下面来分情形讨论一下可能发生的情况:

    • 情形1:被删除的节点为根节点或者颜色为红色,此时删除该节点不影响红黑树的特点。无需操作

    • 情形2:被删除节点为黑色,兄弟节点为红色,如下图:
      在这里插入图片描述
      若删除上图中的x节点,将缺少一个黑节点,与红黑树的性质冲突,所以修改sib颜色为黑色,设置p节点为红色,并进行左旋操作。在进行后续的处理。

    • 情形3:被删除节点为黑色,x节点的兄弟节点的子节点都是黑色,如下图:
      在这里插入图片描述
      x节点是黑色的,兄弟节点(黑色的)的子节点也是黑色的,p节点的颜色无法确定,有可能是红色的,也有可能是黑色的。如果是红色的直接设置为黑色即可,如果为黑色的,则需要将x定位的p节点,在进行处理。

    • 情形4:被删除节点为黑色,x的兄弟节点的右自子节点为黑色。如下图:
      在这里插入图片描述
      情形4的调整为了转变成情形5的情况,来进行处理。

    • 情形5:被删除节点为黑色,x的兄弟节点右子节点为红色。如下图:
      在这里插入图片描述
      sib的左子节点的颜色不确定,可能是红色也可能是黑色,但是对它并没有什么影响,因为变换前后它的上层分支的黑色节点数并没有改变。

    上面的情形只是针对删除的节点是左孩子的情况,进行的分析,被删除的节点也可能是右分支。情况完全相同只不过左右顺序发生了颠倒,不再进行复述。

    至此TreeMap中实现的最重要已经说完了。

    3、总结

    关于红黑树的节点插入操作,首先是改变新节点,新节点的父节点,祖父节点,和新节点的颜色,能在当前分支通过节点的旋转改变的,则通过此种操作,来满足红黑树的特点

    如果当前相关节点的旋转解决不了红黑树的冲突,则通过将红色的节点移动到根节点解决,最后在将根节点设置为黑色

    展开全文
  • 红黑树:https://blog.csdn.net/v_JULY_v/article/category/774945 TreeMap实现:https://blog.csdn.net/chenssy/article/details/26668941
    展开全文
  • https://www.jianshu.com/p/8b372f3a195d/ https://www.jianshu.com/p/047e33fdefd2 转载于:https://www.cnblogs.com/lixiansheng/p/11349411.html

    https://www.jianshu.com/p/8b372f3a195d/

     

    https://www.jianshu.com/p/047e33fdefd2

     

    转载于:https://www.cnblogs.com/lixiansheng/p/11349411.html

    展开全文
  • TreeMap底层实现原理

    2020-11-06 21:57:16
    TreeMap的实现是红黑树算法的实现,所以要了解TreeMap就必须对红黑树有一定的了解,其实这篇博文的名字叫做:根据红黑树的算法来分析TreeMap的实现,但是为了与Java提高篇系列博文保持一致还是叫做TreeMap比较好。...
  • 概述 文章的内容基于JDK1.7进行分析,之所以选用这个版本,是因为1.8的有些类做了改动,增加了阅读的难度,虽然是1.7,但是...如果在调用TreeMap的构造函数时没有指定比较器,则根据key执行自然排序。这点会在接下...
  • TreeMap实现原理

    2018-03-05 13:17:22
    其中有些图画的可能有问题… TreeMap的实现是红黑树算法的实现,所以要了解TreeMap就必须对红黑树有一定的了解,其实这篇博文的名字叫做:根据红黑树的算法来分析TreeMap的实现,但是为了与Java提高篇系列博文保持...
  • TreeMap实现有序的原理

    万次阅读 2019-02-13 14:07:52
    上一篇讲了LinkedHashMap实现有序原理,这票介绍一个另一种有序Map,TreeMap。 同样是有序,两者大不一样,LinkedHashMap是按照插入顺序排序,而...TreeMap还有个好兄弟叫TreeSet,实现原理是一样。 这是...
  • TreeMap继承了NavigableMap接口,NavigableMap接口继承了SortedMap接口,可支持一系列导航定位以及导航操作方法,当然只是提供了接口,需要TreeMap自己去实现TreeMap实现了Cloneable接口,可被克隆,实现了...
  • TreeMap的实现是红黑树算法,要了解TreeMap实现还需要先了解红黑树。 红黑树简介 R-B Tree,全称是Red-Black Tree,又称为“红黑树”,它一种特殊的二叉查找树。红黑树的每个节点上都有存储位表示节点的颜色,可以...
  • TreeMap的实现是红黑树算法的实现,所以要了解TreeMap就必须对红黑树有一定的了解,其实这篇博文的名字叫做:根据红黑树的算法来分析TreeMap的实现,但是为了与Java提高篇系列博文保持一致还是叫做TreeMap比较好。...
  • 一、TreeMap 实现降序排列的原理TreeMap 底层为数据结构为红黑树,默认为升序排序方式。整个红黑树结构为:根节点值大于所有左子树节点值,小于所有右子树节点值,由此整个红黑树以深度优先搜索方式遍历一遍为从小...
  • TreeMap的实现是红黑树算法的实现,所以要了解TreeMap就必须对红黑树有一定的了解,通过这篇博文你可以获得如下知识点: 1. 红黑树的基本概念。 2. 红黑树增加节点、删除节点的实现过程。 3. 红黑树左旋转、右...
  • 前面我们分别讲了Map接口的两个实现类HashMap和LinkedHashMap,本章我们讲一下Map接口另一个重要的实现TreeMapTreeMap或许不如HashMap那么常用,但存在即合理,它也有自己的应用场景,TreeMap可以实现元素的自动...
  • TreeMap的底层原理

    千次阅读 2018-11-02 14:39:31
    TreeMap是桶+红黑树的实现方式.TreeMap的底层结构就是一个数组,数组中每一个元素又是一个红黑树.当添加一个元素(key-value)的时候,根据key的hash值来确定插入到哪一个桶中(确定插入数组中的位置),当桶中有多个元素时...
  • TreeMap的工作原理

    2019-05-22 09:43:58
    java中的TreeMap是如何通过put,deleteEntry两个来实现红黑树增加。删除节点。 基本概念 红黑树首先是一颗二叉树,它具有二叉树特性,同时红黑树更是一颗自平衡排序二叉树。 二叉树特性: 任何节点值大于...
  • 一、简介 TreeMap是一个线程不安全,有序的键值对集合,因为TreeMap实现了SotredMap...此外,TreeMap的Key是不能为null的,因为要实现排序。而Value是允许为null的。 二、数据结构 /** * TreeMap */ public class Tree
  • TreeMap实现原理及源码分析

    千次阅读 2017-08-01 09:12:34
    TreeMap是一个有序key-value集合,基于红黑树(Red-Black tree)实现。该映射根据其键自然顺序进行排序,或者根据创建时提供Comparator进行排序、 对于TreeMap而言,每个Entry都被当成“红黑树”一个节点对待...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 580
精华内容 232
关键字:

treemap的实现原理