前言

  最近看到好多红黑树的东西,英文好的童鞋可以直接点击http://www.cs.princeton.edu/~rs/talks/LLRB/RedBlack.pdf这里查看我之前学习的材料,对理解下面讲的东西肯定也有点帮助(但是不完全一样),英文一般的同学就直接看我的文采飞扬把哈哈。还有大家可以去coursera上学习一些国外比较好的资料。感觉比国内一些学习网站做的好很多。

  前面一篇随笔写的binarysearchtree(http://www.cnblogs.com/robsann/p/7567596.html)说了有一个缺点就是不平衡,意思就是插入已经排好序的对象的时候会变成一条链表,链表当然比二叉树要慢很多啦,随机插入的话二叉树的各种方法需要的时间和LgN的成正比。所以红黑树其实是在解决二叉树不平衡的问题的。

  度娘(这里稍微看一下)

  红黑树是每个节点都带有颜色属性的二叉查找树,颜色或红色或黑色。在二叉查找树强制一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求:
  性质1. 节点是红色或黑色。
  性质2. 根节点是黑色。
  性质3 每个叶节点(NIL节点,空节点)是黑色的。
  性质4 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
  性质5. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
  这些约束强制了红黑树的关键性质: 从根到叶子的最长的可能路径不多于最短的可能路径的两倍长。结果是这个树大致上是平衡的。因为操作比如插入、删除和查找某个值的最坏情况时间都要求与树的高度成比例,这个在高度上的理论上限允许红黑树在最坏情况下都是高效的,而不同于普通的二叉查找树。

 正文 

  2 - 3 - 4 Tree

  2 - 3 - 4 Tree 我觉得算是一种模型,一种树模型保证了树是平衡的,所谓平衡就是树不会一个枝头长得很高,另外一个枝头长得很矮,那保证平衡有什么用?平衡的情况下,所有操作需要的时间都是和LgN成正比的,你说腻害不腻害。

  2 - 3 - 4 树,允许一个节点是 2-nodes 或者是 3 nodes 或者是 4 nodes, 具体的意思就是说

   这是一个2-nodes, 有2个触手,能够指向不同的2个子元素,左边的子元素小于A,右边的子元素大于A

  3-nodes, 3 分叉,最左边的子元素小于C,中间的子元素between c和e, 最右边的子元素大于E

    4-nodes, 3分叉。同上

  看看2-3-4tree是如何保证平衡的

  假设有 10 7 6 3 8 11 15 要插入 2 -3 4 树中

  

  

  

  

  

  

  这里插入还有另外一种选择叫Bottom-up solution,之下而上的一种解法(可以忽略)。就是先找到这个节点会被插入的位置,如果插入后变成了5-nodes,就把其中一个节点往父节点抛出,让父节点和其中的一个子节点结合。

  删除同理,为了要保证树的平衡,当删除的时候,如果被删除的节点只有一个元素的话,必须要把父亲元素拉下来形成一个3-nodes然后删除后变成2-nodes(后面还会说)

  左倾红黑树(LeftLeaningRedBlackTree)

  红黑树和2-3-4树的关系,红黑树是2-3-4树的一种实现方式。2-3-4树只是一个模型。

  先介绍一下节点对象Node,看代码,之后会用到这个对象

    private class Node {
private K k; // 这个节点的key 
private V v; //节点的value
private Node left, right; //左右节点
private int size;  //节点为根的树的大小
private boolean color; //节点的颜色,分黑和红 Node(K k, V v, int size, boolean color) {
this.k = k;
this.v = v;
this.size = size;
this.color = color;
} @Override
public String toString() {
if (color) return "red " + v;
else return "black " + v;
} }

Node(节点对象)

  左倾红黑树和红黑树的区别如下

  这是一般的红黑树,3-node 的时候红节点可以左倾或者右倾

  左边是2-3-4树的模型,右边是红黑树的实现

  

  这是左倾红黑树, 3-nodes的时候红色的节点必须在左边。

  

  用常量来表示红黑

  

  红黑树的修正

  首先左倾红黑树的性质必须要得到保证(就是上图中说的Require that 3 -node be left leaning),但是很多时候可能因为插入或者删除的操作破坏了这个性质,所以我们必须要修正。

  这里介绍3个修正的方法

  第一个方法的使用场景是这样子的,(为了不破坏平衡,每次插入的都是红节点),下图插入的节点在右边,破坏了左倾的性质,所以必须rotateLeft。rotateLeft是指把红色节点左移

    

  方法如下

    //右树是红link的时候,turn this red link to left
private Node rotateLeft(Node h) {
assert(isRed(h.right)); Node x = h.right; //change the pointers
h.right = x.left;
x.left = h; x.color = h.color; //change the colors
h.color = RED; x.size = h.size; //change the sizes
h.size = size(h.left) + size(h.right) + 1;
return x;
}

rotateLeft(仔细看一边)

  

  另外一种情况是最新插入的节点在最左边,把中间节点rotateRight,重新平衡

    

  方法如下

    //左树是红link的时候,turn this red link to left
private Node rotateRight(Node h) {
assert(isRed(h.left)); Node x = h.left;
h.left = x.right;
x.right = h; x.color = h.color;
h.color = RED; x.size = h.size; //size is the same
h.size = size(h.left) + size(h.right) + 1; return x;
}

rotateRight

  这张图是破坏了性质之后,修正的办法

  

  还有一个方法是flipColors(),代码如下,就是可能插入的时候需要满足当前节点不是4-nodes,可能就会使用这个方法

  

  有了上面这些辅助的方法后就可以开始下面的学习了

  

  左倾红黑树的put  

  在2-3-4tree中的介绍中也知道了,put的时候,当前节点如果是4-nodes的话就没有位置留给需要插入的对象了。

  所以我们在put的时候,一定要保证当前的节点(currentNode)以后用cn来表示。cn必须不是4-nodes, 如果是4-nodes的话就用flipColor把4-node变成3个2-node

  假设我们要插入10  7  6  3 这4个对象, 大片动态图,燃烧的经费。

  

  附上代码

    public void put(K k, V v) {
root = put(root, k, v);
root.color = BLACK;
} private Node put(Node cn, K k, V v) {
if (cn == null) return new Node(k, v, 1, RED);
if(isRed(cn.left) && isRed(cn.right)) split4Node(cn);//是4节点的话 就split int cmp = k.compareTo(cn.k);
if (cmp > 0) cn.right = put(cn.right, k, v); // k > node.k go right
else if (cmp < 0) cn.left = put(cn.left, k, v);
else cn.v = v; //hit //following code is to fix the tree on the way up
if (isRed(cn.right) && !isRed(cn.left)) cn = rotateLeft(cn); // right leaning 3nodes的时候   需要变成 left leaning
if (isRed(cn.left) && isRed(cn.left.left)) cn = rotateRight(cn); //变成了一个4节点 cn.size = size(cn.left) + size(cn.right) + 1;
return cn;
}

put

  

  左倾红黑树的get

  树的get方法其实很简单,就是判断key是不是相等,如果相等就return 这个值。

  

    public V get(K k) {
return get(root, k);
} //cn means currentNode
private V get(Node cn, K k) {
if (cn == null) return null; // not find the key int cmp = k.compareTo(cn.k);
if(cmp > 0) return get(cn.right, k);
else if (cmp < 0) return get(cn.left, k);
else return cn.v; // hit
}

get

  左倾红黑树的删除

  删除可以说是最难的了把,基本的思想就是,cn节点(当前节点)不会是2-node,(如果root节点是2-nodes,我们需要把root节点变成红节点)

  保证其中一个子节点不是2节点(这个保证需要看删除的节点位于当前节点的哪里,比如删除的节点比cn节点小,所以接下来我们会往left走,所以要保证left节点不是2-node)。因为2节点如果删除了的话就不会平衡。所以必须要把红节点从root一步一步carry下去。

  先实现一个deleteMin方法,我们要把红节点带向左边。想一想,红节点带向左边后,如果左边有节点删除了可能没办法保持平衡,红节点可以变成黑节点,代替刚才被删除的节点。通过这样子可以保证左边的树是一定会平衡的。

  deleteMin的几种情况

  1. , 现在可以直接删除掉。也不会影响平衡。删除了后3节点变成了2节点。

  

  2., 需要继续向左走,但是左子节点是2-node,必须要想办法变成不是2-node。所以需要把父亲节点和兄弟节点和cn节点。整合在一起变成4-node

  

  3. , 需要继续向左走,但是左子节点是2-node,必须要想办法变成不是2-node。发现兄弟节点是不是2-nodes。所以把兄弟节点借一个node过来

  

  

  2和3这2种情况总结在一起就是moveRedLeft的代码

  

    public void deleteMin() {
//保证了root节点不是2nodes
if (!isRed(root.left) && !isRed(root.right))
root.color = RED; root = deleteMin(root);
root.color = BLACK;
} public Node deleteMin(Node cn) {
if (cn.left == null) return null; if (!isRed(cn.left) && !isRed(cn.left.left)) //判断左边子节点是不是2node,是的话就需要把Red带下去
cn = moveRedLeft(cn); cn.left = deleteMin(cn.left); return fixup(cn);
} private Node fixup(Node h) { if (isRed(h.right) && !isRed(h.left)) h = rotateLeft(h); //右倾
if (isRed(h.left) && isRed(h.left.left)) h = rotateRight(h);
h.size = size(h.left) + size(h.right) + 1; //right the size
return h;
}

deleteMin

  随意的delete方法。按照下面的图说一下,基本的思想。

  首先删除D。从H开始,H不是2-node(右边有一个红节点),D小于H,左往H的左边找

  找到了D。D不是2-node且是红节点。找到了D,处理办法是找到右树中最小的值,发现是E, 把最小的值赋值给当前的node

  所以我们要往右边走。但是发现右边节点F是2-node。所以我们要把红色的连接往右边带。

  flipColor(D), B D 和 F就变成了一个4-node(可以自己做一下图看看是不是这样子的)。 这时候红链接也往右边带了。

  现在到了F 节点。发现F 的左节点是 2-node。把红色的链接往左边带。

  flipColor(F),这个时候F 和 E 和 G ,变成了一个4-node。 顺理成章删除左边的节点。没有影响平衡。

  接着原路返回,修复节点。

  

  

  

    public void delete(K k) {
if (k == null) throw new IllegalArgumentException("argument to delete() is null");
if (!contains(k)) return; if (!isRed(root.left) && !isRed(root.right))
root.color = RED;
root = delete(root, k);
if (root != null)
root.color = BLACK;
} public boolean contains(K k) {
return get(k) != null;
} private Node delete(Node cn, K k) { if (cn == null) return null; int cmp = k.compareTo(cn.k); if (cmp < 0) { // k < node.k go left
if (!isRed(cn.left) && !isRed(cn.left.left)) //保证了下一个左元素不是2nodes
cn = moveRedLeft(cn);
cn.left = delete(cn.left, k);
} else if (cmp > 0) { // k > node.k go right
if (isRed(cn.left) && !isRed(cn.right)) //如果是3节点的话需要 rotate 把red转到右边
cn = rotateRight(cn);
if (!isRed(cn.right) && !isRed(cn.right.left)) //保证下一个右节点不是2nodes
cn = moveRedRight(cn);
cn.right = delete(cn.right, k);
} else { //hit if (isRed(cn.left) && !isRed(cn.right))
cn = rotateRight(cn); if (k.compareTo(cn.k) == 0 && (cn.right == null)) //find null just return null
return null; if (!isRed(cn.right) && !isRed(cn.right.left)) //保证下一个右节点不是2nodes
cn = moveRedRight(cn); if (k.compareTo(cn.k) == 0) {
Node x = min(cn.right);
cn.k = x.k;
cn.v = x.v;
cn.right = deleteMin(cn.right);
} else cn.right = delete(cn.right, k);
}
return fixup(cn);
}

delete

 总结

  具体的实现可以参考一下https://github.com/Cheemion/algorithms/blob/master/src/com/algorithms/tree/LeftLeaningRedBlackTree.java

  可能有地方说的不清楚哈。见谅,可以留言我有不清楚的地方

  

Tree--RedBlackTree详解(2 - 3 - 4Tree)(红黑树)的更多相关文章

  1. Ext.Net学习笔记22:Ext.Net Tree 用法详解

    Ext.Net学习笔记22:Ext.Net Tree 用法详解 上面的图片是一个简单的树,使用Ext.Net来创建这样的树结构非常简单,代码如下: <ext:TreePanel runat=&q ...

  2. linux shell 脚本攻略学习16--wc命令详解,tree命令详解

    在文本处理的工作中,统计文件的行数,单词数和字符数非常有用.而对于开发人员本身来说,统计LOC(line of code ,代码行数)是一件重要的工作.linux中有什么命令可以帮助我们做统计呢?没错 ...

  3. CentOS tree命令详解

    inux下tree命令详解---linux以树状图逐级列出目录的内容命令 ############################################################### ...

  4. Merkle Tree算法详解

    转载自:http://blog.csdn.net/yuanrxdu/article/details/22474697Merkle Tree是Dynamo中用来同步数据一致性的算法,Merkle Tre ...

  5. Linux tree命令详解

    tree: 查看目录结构 tree常见命令参数 usage: tree [-adfghilnpqrstuvxACDFNS] [-H baseHREF] [-T title ] [-L level [- ...

  6. tree命令详解

    基础命令学习目录首页 原文链接:http://man.linuxde.net/tree -a:显示所有文件和目录:-A:使用ASNI绘图字符显示树状图而非以ASCII字符组合:-C:在文件和目录清单加 ...

  7. Linux:tree命令详解

    tree 以树状图列出目录的内容 语法 tree(选项)(参数) 选项 -a:显示所有文件和目录: -A:使用ASNI绘图字符显示树状图而非以ASCII字符组合: -C:在文件和目录清单加上色彩,便于 ...

  8. Kd Tree算法详解

    kd树(k-dimensional树的简称),是一种分割k维数据空间的数据结构,主要应用于多维空间关键数据的近邻查找(Nearest Neighbor)和近似最近邻查找(Approximate Nea ...

  9. 『现学现忘』Git对象 — 16、Tree对象详解

    目录 1.Tree对象介绍 2.Tree对象说明 (1)初始化一个新的本地版本库 (2)创建一个树对象(重点) (3)创建第二个文件(重点) (4)将第一个树对象加入暂存区,使其成为新的树对 3.总结 ...

随机推荐

  1. JavaScript封装一个实用的select控件

    最近一直把精力放在项目上面,导致忽略的一些底层的东西.以前就一直觉得原有的select控件很丑,正好周末有时间,试着做了一个简单封装,实现了它的基本功能.我总结了一下,大概分为三个部分: 1.对显示样 ...

  2. netcore2.0 ORM框架中如何配置自定义的主外键加载

    环境:netcore2.0 DB :mysql ORM:Ant https://github.com/yuzd/AntData.ORM/tree/netcore2 [给我一个star吧] NUGET: ...

  3. EAGO科技人工智能+澳洲MSPL外汇平台招商

    天下财经 首席运营商 EAGO外汇人工智能简介:我们将历史数据导入算法引擎,加入因子(外汇中使用的各种技术指标及各个国家的货币政策等)让计算机通过二元遗传基因.深度神经网络等算法,由机器自主深度学习. ...

  4. MySql数据库导入导出

    1.导出整个数据库     mysqldump -u 用户名 -p 数据库名 > 存放位置     比如:     mysqldump -u root -p project > c:/a. ...

  5. JSP页面中<%!%>与<%%>与<%=%>

    首先,我们要了解jsp运行原理.JSP的本质就是一个Servlet,JSP的运行之前会先被Tomcat服务器翻译为.java文件,然后在将.java文本编译 为.class文件,而我们在访问jsp时, ...

  6. SynchronizedMap和ConcurrentHashMap 区别

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt215 SynchronizedMap和ConcurrentHashMap的深 ...

  7. Arrays和Collections 对于sort的不同实现原理

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcytp46 1.Arrays.sort() 该算法是一个经过调优的快速排序,此算法 ...

  8. python 从视频中提取图片,并保存在硬盘上

    使用python的moviepy库来提取视频中的图片,按照视频每帧一个图片的方式来保存. extract images from video, than save them to disk from ...

  9. 201521123083《Java程序设计》第13周学习总结

    本次作业参考文件 正则表达式参考资料 1. 本周学习总结 以你喜欢的方式(思维导图.OneNote或其他)归纳总结多网络相关内容. 2. 书面作业 1. 网络基础 1.1 比较ping www.bai ...

  10. 团队作业八——第二次团队冲刺(Beta版本)第6天

    团队作业八--第二次团队冲刺(Beta版本)第6天 一.每个人的工作 (1) 昨天已完成的工作 简单模式逻辑代码涉及与相关功能的具体实现 (2) 今天计划完成的工作 修改完善注册登录内容界面,编辑错题 ...