- 浏览: 113822 次
- 性别:
- 来自: 成都
文章分类
最新评论
TreeMap的实现
TreeMap使用红黑二叉树实现。
红黑二叉树:
a.根节点是黑色的;
b.红色节点的儿子节点是黑色的;
c. 任何一个节点到空节点的所有路径上必包含相同数据的黑色节点;
d. 叶子节点的子节点是黑色节点
假设一颗红黑树的黑色节点个数为R,那么这棵树的最短高度为R,最大长度为2R
所以h<2R,R的最大值为log(n+1),所以红黑树的高度<2log(n+1)
先看插入:第一步,先找到新节点在红黑二叉排序树中的位置,并插入:
- intcmp;
- Entry<K,V>parent;
- //splitcomparatorandcomparablepaths
- Comparator<?superK>cpr=comparator;
- if(cpr!=null){
- do{
- parent=t;
- cmp=cpr.compare(key,t.key);
- if(cmp<0)
- t=t.left;
- elseif(cmp>0)
- t=t.right;
- else
- returnt.setValue(value);
- }while(t!=null);
- }
- else{
- if(key==null)
- thrownewNullPointerException();
- Comparable<?superK>k=(Comparable<?superK>)key;
- do{
- parent=t;
- cmp=k.compareTo(t.key);
- if(cmp<0)
- t=t.left;
- elseif(cmp>0)
- t=t.right;
- else
- returnt.setValue(value);
- }while(t!=null);
- }
- Entry<K,V>e=newEntry<K,V>(key,value,parent);
- if(cmp<0)
- parent.left=e;
- else
- parent.right=e;
int cmp; Entry<K,V> parent; // split comparator and comparable paths Comparator<? super K> cpr = comparator; if (cpr != null) { 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<K,V>(key, value, parent); if (cmp < 0) parent.left = e; else parent.right = e;上述代码无需多做分析,new TreeMap的时候可以指定具体的Comparator或者使用系统默认的Comparable接口,然后按照key从根节点开始找,直到具体可以插入的接口,插入即可;
第二步,因为二叉树中新增了一个节点,如果红黑树的平衡性受了影响,就需要进行调整:
新插入的节点先标成红色,如果该插入的节点不是根节点而且新节点的父节点是黑色的话,那么新加节点后,没有违法上述abcd四点,红黑二叉排序树就不需要再做任何调整。所以只有当新插入节点非root节点或者其对应的父节点是红色节点的时候才需要重新调整:
- while(x!=null&&x!=root&&x.parent.color==RED)
while (x != null && x != root && x.parent.color == RED)如果新节点X对应的父节点是个左孩子节点的话,会进行以下调整:
取到新节点父节点的兄弟节点y,如果兄弟节点是红色,那么就将x和y标记为黑色,x父节点和其兄弟节点y对应的父节点标记为红色,此时因为他们的父节点的父节点也有可能是红色的,所以需要将父节点赋值给x,重新对x向上进行调整:
- Entry<K,V>y=rightOf(parentOf(parentOf(x)));
- if(colorOf(y)==RED){
- setColor(parentOf(x),BLACK);
- setColor(y,BLACK);
- setColor(parentOf(parentOf(x)),RED);
- x=parentOf(parentOf(x));
- }
Entry<K,V> y = rightOf(parentOf(parentOf(x))); if (colorOf(y) == RED) { setColor(parentOf(x), BLACK); setColor(y, BLACK); setColor(parentOf(parentOf(x)), RED); x = parentOf(parentOf(x)); }否则如果兄弟节点y是黑色的:
如果此时x是左节点的话,就要将x父节点设置为黑色,x父节点的父节点设置为红色,然后对x父节点的父节点右旋即可,即:将x父节点的父节点极其孩子节点设置为x父节点的右子树,如果x是右节点的话,要先将x对应的父节点设置为x的左孩子,此时问题就和x是左节点是一样的了,只是此时的x已经是x的父节点而已,其代码如下
- else{
- if(x==rightOf(parentOf(x))){//如果x是右节点的话,要先将x对应的父节点设置为x的左孩子
- x=parentOf(x);
- rotateLeft(x);
- }
- setColor(parentOf(x),BLACK);//x父节点设置为黑色
- setColor(parentOf(parentOf(x)),RED);//x父节点的父节点设置为红色
- rotateRight(parentOf(parentOf(x)));//x父节点的父节点右旋成为x父节点的右孩子
- }
else { if (x == rightOf(parentOf(x))) {//如果x是右节点的话,要先将x对应的父节点设置为x的左孩子 x = parentOf(x); rotateLeft(x); } setColor(parentOf(x), BLACK);//x父节点设置为黑色 setColor(parentOf(parentOf(x)), RED);//x父节点的父节点设置为红色 rotateRight(parentOf(parentOf(x)));//x父节点的父节点右旋成为x父节点的右孩子 }
如果新节点x对应的父节点是右孩子节点的话,其调整规则和上述是雷同的,在此不再累述,见详细代码:
- else{
- 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);
- rotateRight(x);
- }
- setColor(parentOf(x),BLACK);
- setColor(parentOf(parentOf(x)),RED);
- rotateLeft(parentOf(parentOf(x)));
- }
- }
else { 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); rotateRight(x); } setColor(parentOf(x), BLACK); setColor(parentOf(parentOf(x)), RED); rotateLeft(parentOf(parentOf(x))); } }右旋转的代码如下:
- privatevoidrotateRight(Entry<K,V>p){
- if(p!=null){
- Entry<K,V>l=p.left;
- p.left=l.right;
- if(l.right!=null)l.right.parent=p;
- l.parent=p.parent;
- if(p.parent==null)
- root=l;
- elseif(p.parent.right==p)
- p.parent.right=l;
- elsep.parent.left=l;
- l.right=p;
- p.parent=l;
- }
- }
private void rotateRight(Entry<K,V> p) { if (p != null) { Entry<K,V> l = p.left; p.left = l.right; if (l.right != null) l.right.parent = p; l.parent = p.parent; if (p.parent == null) root = l; else if (p.parent.right == p) p.parent.right = l; else p.parent.left = l; l.right = p; p.parent = l; } }
效果就是将当前节点的设置为其子节点的右节点,并将子节点节点原来的右节点设置为当前节点的左子节点;
左旋转的效果就是将当前节点设置为右子节点的左节点,右子节点原来的左子节点设置为当前节点的右子节点,代码如下:
- privatevoidrotateLeft(Entry<K,V>p){
- if(p!=null){
- Entry<K,V>r=p.right;
- p.right=r.left;
- if(r.left!=null)
- r.left.parent=p;
- r.parent=p.parent;
- if(p.parent==null)
- root=r;
- elseif(p.parent.left==p)
- p.parent.left=r;
- else
- p.parent.right=r;
- r.left=p;
- p.parent=r;
- }
- }
private void rotateLeft(Entry<K,V> p) { if (p != null) { Entry<K,V> r = p.right; p.right = r.left; if (r.left != null) r.left.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.left = p; p.parent = r; } }
TreeMap删除元素:
和新增元素一样,删除元素也可能会破坏二叉树的平衡性,所以也可能需要做调整,查找过程就不说了,看看删除的时候我们如何调整来维护其平衡性的:
假如当前要被删除节点有两个孩子节点,那么只需要将其后继节点查找出来,将其内容换成后继节点的内容,然后问题就转变成删除后继节点的问题.这里是找到当前节点的右子树中最小的那个节点的内容替代之,
- Entry<K,V>p=t.right;
- while(p.left!=null)
- p=p.left;
- returnp;
Entry<K,V> p = t.right; while (p.left != null) p = p.left; return p;
这样删除有两个孩子节点的节点问题就转换成删除带有一个子节点或者删除叶子节点的问题。接下来一段代码是:
- //Startfixupatreplacementnode,ifitexists.
- Entry<K,V>replacement=(p.left!=null?p.left:p.right);
// Start fixup at replacement node, if it exists. Entry<K,V> replacement = (p.left != null ? p.left : p.right);假如这里的p是上述后继节点得来,因为此时p不可能有左孩子,那替代者就是p.right,此时可能为null,或者是被删除节点的子节点少于两个的情况,可能是p.left,也可能是p.right.如果不是null的话就要将后继节点指向到p的parent节点,用于删除当前节点:
- replacement.parent=p.parent;
- if(p.parent==null)
- root=replacement;
- elseif(p==p.parent.left)
- p.parent.left=replacement;
- else
- p.parent.right=replacement;
replacement.parent = p.parent; if (p.parent == null) root = replacement; else if (p == p.parent.left) p.parent.left = replacement; else p.parent.right = replacement;此时p被删除,假如p是红色的,那就不会影响红黑树,但是如果被删除的节点是黑色的,此时因为删掉了一个黑色节点,所以需要对原来的树进行调整:
- while(x!=root&&colorOf(x)==BLACK){
- 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));
- }
- if(colorOf(leftOf(sib))==BLACK&&
- colorOf(rightOf(sib))==BLACK){//如果兄弟节点的两个儿子都是黑色
- setColor(sib,RED);//兄弟节点设置为红色
- x=parentOf(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;
- }
- }
- setColor(x,BLACK);
while (x != root && colorOf(x) == BLACK) { 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)); } if (colorOf(leftOf(sib)) == BLACK && colorOf(rightOf(sib)) == BLACK) {//如果兄弟节点的两个儿子都是黑色 setColor(sib, RED);//兄弟节点设置为红色 x = parentOf(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; } } setColor(x, BLACK);
另外,如果replacement值是空的,就说明被删除节点没有子节点,如果是黑色节点需要进行调整再删除,如果不是就直接删除即可
- if(p.color==BLACK)
- fixAfterDeletion(p);
- if(p.parent!=null){
- if(p==p.parent.left)
- p.parent.left=null;
- elseif(p==p.parent.right)
- p.parent.right=null;
- p.parent=null;
- }
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; }
因为TreeMap的查找算法是很简单的,所以在此不再累述。另外,TreeMap的一个特殊用途是它可以实现数据的区间范围查询
相关推荐
标题中的“treeMap实现分组数据树形结构”指的是利用`TreeMap`的特性,将数据根据某个或某些键值进行分组,并以树形结构展示,每个节点代表一个分组,其子节点是该分组内的数据。这种结构有助于快速查找和访问具有...
Java高级数据结构算法知识
TreeMap实现了SortedMap接口,而TreeSet实现了SortedSet接口。这两个类都使用AVL树来存储数据,从而提供了高效的搜索、插入和删除操作。 三、AVLTreeMap类的实现 AVLTreeMap类是TreeMap类的扩展,它提供了更多的...
C#编程语言提供了多种工具和库来实现这一目标,其中Treemap和Sunburst是两种非常有效的可视化方法。本文将深入探讨这两种算法的原理、实现方式以及它们在C#中的应用。 ### TreeMap算法 TreeMap是一种基于分层数据...
在Java中,TreeMap排序算法的实现可以通过两种方式:一是使用TreeMap的自然排序,二是使用Comparator接口来实现自定义的排序规则。自然排序是指根据键值对的自然顺序进行排序,而Comparator接口则可以根据特定的排序...
ECharts 是百度开发的一个基于 JavaScript 的数据可视化库,它提供了丰富的图表类型,包括柱状图、折线图、饼图以及我们关注的 TreeMap。TreeMap 是一种用以表示层次结构数据的可视化方式,通过不同大小的矩形来展示...
如何用TreeMap实现一致性hash? ConcurrentHashMap是如何在保证并发安全的同时提高性能? ConcurrentHashMap是如何让多线程同时参与扩容? LinkedBlockingQueue、DelayQueue是如何实现的? CopyOnWriteArrayList是...
5. **TreeSet实现**:TreeSet同样实现了Set接口,底层通过TreeMap实现,为元素的添加、删除、查找等操作提供基于红黑树的效率。TreeSet的元素会被自动排序,可以设定自定义的Comparator来决定排序方式。 6. **...
在Python中,我们可以借助Matplotlib库或专门的可视化库如squarify来实现TreeMap。 首先,`products.csv`, `aisles.csv`, 和 `departments.csv` 这三个CSV文件可能代表了一个电商或者零售业务的数据集,其中`...
TreeMap是Java集合框架中的一种有序映射数据结构,它实现了SortedMap接口,提供了按自然顺序或自定义比较器顺序存储键值对的能力。在深入理解TreeMap的源码之前,我们首先要了解其背后的基石——红黑树。 红黑树...
在Java编程语言中,`TreeMap` 是一个有序的键值对集合,它实现了 `SortedMap` 接口。这个数据结构内部基于红黑树(Red-Black Tree)算法实现,保证了插入、删除和查找操作的时间复杂度为 O(log n)。在“java treemap...
以下是使用 TreeMap 实现排序的示例代码: ```java import java.util.*; public class Screen implements Comparable<Screen> { private double size, price; @Override public int compareTo(Screen s) { ...
TreeSet是基于TreeMap实现的Set集合,它使用红黑树算法来组织元素。TreeSet提供了元素的自动排序功能,可以按照自然顺序排序,也可以通过提供Comparator来实现定制排序。TreeSet在插入、删除和查找操作时,都能保持O...
《阿里巴巴11》这篇内容主要涉及了两个核心知识点:Top K问题的解决策略以及Java中的TreeMap实现原理。首先,我们来深入理解Top K算法。 Top K问题在数据处理中非常常见,目标是从大量数据中找出最大的K个元素。...
TreeMap是Java集合框架中的一种Map实现,它实现了SortedMap接口,能够根据键的自然顺序或自定义的比较器对键进行排序。在本例中,我们使用TreeMap来统计单词出现的次数,并按照字母表顺序输出。 知识点2:Java比较...
Java中,TreeSet和TreeMap实现了红黑树,而PriorityQueue内部使用了堆数据结构。 3. 图: 图是由节点(顶点)和连接节点的边构成,可以表示复杂的关系。图分为有向图和无向图,Java中没有直接提供图的实现,但可以...
在开发过程中,你可能需要查看Treemap-4.1.2中的源代码来了解这个特定版本的实现细节,如优化、异常处理、线程安全措施等。这些源代码也可能包含了一些示例或者API的使用说明,帮助开发者更好地理解和使用这个特定...
在Java中,可以自定义树结构或使用TreeSet和TreeMap实现。树在搜索、排序和文件系统中广泛应用。 6. **图**:图由顶点和边组成,用于表示对象之间的关系。Java没有内置的图数据结构,通常需要自定义实现。图在路由...