-
红黑树的基本概念
-
增加节点,删除节点的过程
-
左旋转,右旋转的复杂过程
-
java中的TreeMap是如何通过put,deleteEntry两个来实现红黑树增加。删除节点的。
基本概念 -
红黑树首先是一颗二叉树,它具有二叉树的特性,同时红黑树更是一颗自平衡的排序二叉树。
二叉树的特性:
任何节点的值大于它的左节点且小于它的右节点,这样构建二叉树的过程很容易失去平衡,最坏的情况就是一边倒,这样导致对二叉树的检索效率大大降低(0(n)),所以为了维持二叉树的平衡,提出了红黑树。
平衡二叉树的特点:
-
它是一颗空树
-
左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一颗平衡二叉树。
红黑树的特点: -
每个节点都只能是红色或是黑色。
-
根节点是黑色
-
每个叶节点是黑色的
-
如果一个节点是红的,那它的两个子节点都是黑的,也就是说在一条路径不能出现相邻的两个红色节点。
-
从任一节点到其每个叶子的所有路径包含相同数目的黑色节点。
红黑树的三大特性: -
左旋
-
右旋
-
着色
TreeMap的数据结构: -
TreeMap继承AbstractMap,实现Navigable,Cloneable,Serializable三个接口。
-
AbstractMap表明TreeMap为一个Map,所以它支持key-value集合。
-
Navigable既支持一系列的导航方法,具备针对给定搜索目标返回最接近匹配项的导航方法。
为什么有序 -
通过实现comparator接口我们可以对TreeMap的内部排序进行控制。
重要属性:
Entry是treemap的内部类
size:容器大小
modcount:修改的次数
RED:节点的颜色-红色
BLACK:节点的颜色-黑色
Entry内部类的重要属性: -
key 键
-
value 值
-
Entry<k,v> left=null 左孩子
-
Entry<k,v> rigth=nll 右孩子
-
Entry<k,v> parent 父亲
-
boolean color=BLACK 颜色
TreeMap put()方法 -
新节点插入的三个关键地方:
-
插入节点总是红色节点
-
如果插入节点的父节点是黑色的,能维持性质
-
如果插入节点的父节点是红色的,破坏了性质,股插入算法就是通过重新着色或旋转来维持性质。
实现步骤: -
为根节点:
若插入的节点N没有父节点,则直接当做根节点即可,同时将严肃设为黑色。 -
父节点为黑色
这种情况下新节点N同样是直接插入,同时颜色为红色,这是他会存在两个褐色的叶子结点,值为null。由于新增节点为红色,所以通过它的子节点的路径依然会保存着相同的黑色节点数,满足规则5。
-若父节点p和p的兄弟节点U为红色。
对于这种情况直接插入肯定出现不平衡现象。
解决方法:p,u节点变黑,G节点变红,这时经过节点p,u的路径都必须经过G所以在这些路径上面的黑色节点数目还是相同的,但是这时G节点为红色的,这时我们需要将G节点当做新增节点递归处理。
-
若父节点P为红色,叔父节点u为黑色或者缺少,且新增节点n为p节点的右孩子。
解决:对新增节点n,p进行一次左旋转,产生的结果还是不平衡的,这时需进行情况5的操作。
-
父节点p为红色,叔父节点u为黑色或缺少,新增节点为父节点p左孩子。
解决: 这种情况有可能是由于情况四而产生的,也有可能不是。对于这种情况先已P节点为中心进行右旋转,在旋转后产生的树中,节点P是节点N、G的父节点。但是这棵树并不规范,它违反了规则4,所以我们将P、G节点的颜色进行交换,使之其满足规范。开始时所有的路径都需要经过G其他们的黑色节点数一样,但是现在所有的路径改为经过P,且P为整棵树的唯一黑色节点,所以调整后的树同样满足规范5。
treeMap put()方法的分析:
在TreeMap的put()的实现方法中主要分为两个步骤,第一:构建排序二叉树,第二:平衡二叉树。对于排序二叉树的创建,其添加节点的过程如下: 1、以根节点为初始节点进行检索。 2、与当前节点进行比对,若新增节点值较大,则以当前节点的右子节点作为新的当前节点。否则以当前节点的左子节点作为新的当前节点。 3、循环递归2步骤知道检索出合适的叶子节点为止。 4、将新增节点与3步骤中找到的节点进行比对,如果新增节点较大,则添加为右子节点;否则添加为左子节点。 按照这个步骤我们就可以将一个新增节点添加到排序二叉树中合适的位置。如下: 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; //修改次数 + 1 modCount++; return null;