Tree--RedBlackTree详解(2 - 3 - 4Tree)(红黑树)
前言
最近看到好多红黑树的东西,英文好的童鞋可以直接点击http://www.cs.princeton.edu/~rs/talks/LLRB/RedBlack.pdf这里查看我之前学习的材料,对理解下面讲的东西肯定也有点帮助(但是不完全一样),英文一般的同学就直接看我的文采飞扬把哈哈。还有大家可以去coursera上学习一些国外比较好的资料。感觉比国内一些学习网站做的好很多。
前面一篇随笔写的binarysearchtree(http://www.cnblogs.com/robsann/p/7567596.html)说了有一个缺点就是不平衡,意思就是插入已经排好序的对象的时候会变成一条链表,链表当然比二叉树要慢很多啦,随机插入的话二叉树的各种方法需要的时间和LgN的成正比。所以红黑树其实是在解决二叉树不平衡的问题的。
度娘(这里稍微看一下)
正文
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)(红黑树)的更多相关文章
- Ext.Net学习笔记22:Ext.Net Tree 用法详解
Ext.Net学习笔记22:Ext.Net Tree 用法详解 上面的图片是一个简单的树,使用Ext.Net来创建这样的树结构非常简单,代码如下: <ext:TreePanel runat=&q ...
- linux shell 脚本攻略学习16--wc命令详解,tree命令详解
在文本处理的工作中,统计文件的行数,单词数和字符数非常有用.而对于开发人员本身来说,统计LOC(line of code ,代码行数)是一件重要的工作.linux中有什么命令可以帮助我们做统计呢?没错 ...
- CentOS tree命令详解
inux下tree命令详解---linux以树状图逐级列出目录的内容命令 ############################################################### ...
- Merkle Tree算法详解
转载自:http://blog.csdn.net/yuanrxdu/article/details/22474697Merkle Tree是Dynamo中用来同步数据一致性的算法,Merkle Tre ...
- Linux tree命令详解
tree: 查看目录结构 tree常见命令参数 usage: tree [-adfghilnpqrstuvxACDFNS] [-H baseHREF] [-T title ] [-L level [- ...
- tree命令详解
基础命令学习目录首页 原文链接:http://man.linuxde.net/tree -a:显示所有文件和目录:-A:使用ASNI绘图字符显示树状图而非以ASCII字符组合:-C:在文件和目录清单加 ...
- Linux:tree命令详解
tree 以树状图列出目录的内容 语法 tree(选项)(参数) 选项 -a:显示所有文件和目录: -A:使用ASNI绘图字符显示树状图而非以ASCII字符组合: -C:在文件和目录清单加上色彩,便于 ...
- Kd Tree算法详解
kd树(k-dimensional树的简称),是一种分割k维数据空间的数据结构,主要应用于多维空间关键数据的近邻查找(Nearest Neighbor)和近似最近邻查找(Approximate Nea ...
- 『现学现忘』Git对象 — 16、Tree对象详解
目录 1.Tree对象介绍 2.Tree对象说明 (1)初始化一个新的本地版本库 (2)创建一个树对象(重点) (3)创建第二个文件(重点) (4)将第一个树对象加入暂存区,使其成为新的树对 3.总结 ...
随机推荐
- C语言指针详解
前言 这不是我第一次写关于C指针的文章了,只是因为指针对于C来说太重要,而且随着自己编程经历越多,对指针的理解越多,因此有了本文. 为什么需要指针? 指针解决了一些编程中基本的问题. 第一,指针的使用 ...
- 新的表格展示利器 Bootstrap Table Ⅱ
上一篇文章介绍了Bootstrap Table的基本知识点和应用,本文针对上一篇文章中未解决的文件导出问题进行分析,同时介绍BootStrap Table的扩展功能,当行表格数据修改. 1.B ...
- Tomcat启动:Container StandardContext[] has not been started
Container StandardContext[] has not been started\root.xml 初始化失败,检查数据源配置
- MySQL(二)之服务管理与初始化文件修改和连接MySQL
上一篇给大家介绍了怎么在linux和windows中安装mysql,本来是可以放在首页的,但是博客园说“安装配置类文件”不让放在首页.接下来给大家介绍一下在linux和windows下MySQL的一下 ...
- (转载)Java多线程入门理解
转载出处http://blog.csdn.net/evankaka 写在前面的话:此文只能说是java多线程的一个入门,其实Java里头线程完全可以写一本书了,但是如果最基本的你都学掌握好,又怎么能更 ...
- 推荐一款接口文档在线管理系统-MinDoc
项目简介 MinDoc 是一款针对IT团队开发的简单好用的文档管理系统. MinDoc 的前身是 SmartWiki 文档系统.SmartWiki 是基于 PHP 框架 laravel 开发的一款文档 ...
- HK2框架的简单自实现kunJ
kunJ kunJ框架,是基于HK2框架的一个自实现注入框架,功能比较简单,重在探索依赖注入的实现原理. 实现细节 自定义3个注解,Access,Inject,Service 在Service中实现对 ...
- 工具类:将其他编码类型转换成UTF-8或者其他类型的工具类
将其他编码类型转换成UTF-8或者其他类型的工具类 public static String changeUTF(String str) { String newStr = null; try { n ...
- Maven 中央仓库及阿里云仓库地址
Maven 中央仓库地址: 1. http://www.sonatype.org/nexus/ 2. http://mvnrepository.com/ 3. http://repo1.maven.o ...
- 利用百度地图API和群蚁算法,对TSP问题进行模拟与求解
前言 最近由于换了工作,期间也有反思和总结上家公司的得失,总觉得有什么事情当初可以完成或者完成得更好,其中TSP问题就是其中之一.当初在开发一个仓配系统的时候,有一个线路排程的需求,当时自己简单在纸上 ...