概念

  红黑树是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组。它是在1972年由Rudolf Bayer发明的,他称之为"对称二叉B树",它现代的名字是在 Leo J. Guibas 和 Robert Sedgewick 于1978年写的一篇论文中获得的。它是复杂的,但它的操作有着良好的最坏情况运行时间,并且在实践中是高效的: 它可以在O(log n)时间内做查找,插入和删除,这里的n 是树中元素的数目。

  红黑树是一种很有意思的平衡检索树。它的统计性能要好于平衡二叉树(有些书籍根据作者姓名,Adelson-Velskii和Landis,将其称为AVL-树),因此,红黑树在很多地方都有应用。在C++ STL中,很多部分(目前包括set, multiset, map, multimap)应用了红黑树的变体(SGI STL中的红黑树有一些变化,这些修改提供了更好的性能,以及对set操作的支持)。

背景和术语

红黑树是一种特定类型的二叉树,它是在计算机科学中用来组织数据比如数字的块的一种结构。所有数据块都存储在节点中。这些节点中的某一个节点总是担当启始位置的功能,它不是任何节点的儿子;我们称之为根节点或根。它有最多两个"儿子",都是它连接到的其他节点。所有这些儿子都可以有自己的儿子,以此类推。这样根节点就有了把它连接到在树中任何其他节点的路径。

如果一个节点没有儿子,我们称之为叶子节点,因为在直觉上它是在树的边缘上。子树是从特定节点可以延伸到的树的某一部分,其自身被当作一个树。在红黑树中,叶子被假定为 null 或空。

由于红黑树也是二叉查找树,它们当中每一个节点都的比较值都必须大于或等于在它的左子树中的所有节点,并且小于或等于在它的右子树中的所有节点。这确保红黑树运作时能够快速的在树中查找给定的值。

用途和好处
        红黑树和AVL树一样都对插入时间、删除时间和查找时间提供了最好可能的最坏情况担保。这不只是使它们在时间敏感的应用如即时应用(real time application)中有价值,而且使它们有在提供最坏情况担保的其他数据结构中作为建造板块的价值;例如,在计算几何中使用的很多数据结构都可以基于红黑树。

红黑树在函数式编程中也特别有用,在这里它们是最常用的持久数据结构之一,它们用来构造关联数组和集合,在突变之后它们能保持为以前的版本。除了O(log n)的时间之外,红黑树的持久版本对每次插入或删除需要O(log n)的空间。

红黑树是 2-3-4树的一种等同。换句话说,对于每个 2-3-4 树,都存在至少一个数据元素是同样次序的红黑树。在 2-3-4 树上的插入和删除操作也等同于在红黑树中颜色翻转和旋转。这使得 2-3-4 树成为理解红黑树背后的逻辑的重要工具,这也是很多介绍算法的教科书在红黑树之前介绍 2-3-4 树的原因,尽管 2-3-4 树在实践中不经常使用。

属性
        红黑树是每个节点都有颜色特性的二叉查找树,颜色的值是红色或黑色之一。除了二叉查找树带有的一般要求,我们对任何有效的红黑树加以如下增补要求:

1.节点是红色或黑色。

2.根是黑色。

3.所有叶子(外部节点)都是黑色。

4.每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)

5.从每个叶子到根的所有路径都包含相同数目的黑色节点。

这些约束强制了红黑树的关键属性: 从根到叶子的最长的可能路径不多于最短的可能路径的两倍长。结果是这个树大致上是平衡的。因为操作比如插入、删除和查找某个值都要求与树的高度成比例的最坏情况时间,这个在高度上的理论上限允许红黑树在最坏情况下都是高效的,而不同于普通的二叉查找树。

要知道为什么这些特性确保了这个结果,注意到属性4导致了路径不能有两个毗连的红色节点就足够了。最短的可能路径都是黑色节点,最长的可能路径有交替的红色和黑色节点。因为根据属性5所有最长的路径都有相同数目的黑色节点,这就表明了没有路径能多于任何其他路径的两倍长。

在很多树数据结构的表示中,一个节点有可能只有一个儿子,而叶子节点包含数据。用这种范例表示红黑树是可能的,但是这会改变一些属性并使算法复杂。为此,本文中我们使用 "nil 叶子" 或"空(null)叶子",如上图所示,它不包含数据而只充当树在此结束的指示。这些节点在绘图中经常被省略,导致了这些树好像同上述原则相矛盾,而实际上不是这样。与此有关的结论是所有节点都有两个儿子,尽管其中的一个或两个可能是空叶子。

操作
        在红黑树上只读操作不需要对用于二叉查找树的操作做出修改,因为它也二叉查找树。但是,在插入和删除之后,红黑属性可能变得违规。恢复红黑属性需要少量(O(log n))的颜色变更(这在实践中是非常快速的)并且不超过三次树旋转(对于插入是两次)。这允许插入和删除保持为 O(log n) 次,但是它导致了非常复杂的操作。

实现
红黑树可能是要考虑情况最多的BST树了,它有自己的规则(见代码的注释),通过这些规则可以保证花费较小的代价来达到相对平衡。
注意,红黑树仍然不是平衡树,但是统计性能要好于AVL树。
要保持红黑树的规则,主要通过两类操作,一类是换色,一类还是旋转。
红黑树插入主要要解决红-红冲突,而删除主要则解决“双黑”

同样,红黑树的删除节点实现是最复杂的,不过,复杂也就在于考虑的情况多,掌握了这几种情况实现还是不困难。
其实,红黑树其实是一颗扩充的二叉树,所以也是满二叉树,其空节点可以看做是扩充的叶节点。但是红黑树的扩充叶节点是有特殊意义的。

下面是代码:

package algorithms.tree;

/**
 * R-B Tree has following four rules:
 * 1)every node is either red or black
 * 2)root and empty node (external leaf node) are always black.
 * 3)the red node's parent node must be black
 * 4)every simple path start from node X to its descendant contains same number of black node
 * 
 * 
 * 
@author yovn
 *
 */

public class RBTree<E extends Comparable<E>> extends DefaultBSTree<E> implements BSTree<E> {

public static class RBPrinter<E extends Comparable<E>> implements DefaultBSTree.NodeVisitor<E>
    {

@Override
        
public void visit(E ele) {
            
            
        }

@Override
        public void visitNode(algorithms.tree.DefaultBSTree.BSTNode<E> node) {
            RBNode<E> n=(RBNode<E>)node;
            if(!n.isNull())
            System.out.print(n.key+"("+(n.color==RBNode.RED?"RED":"BLACK")+"),");
            
        }
        
    }
    static class RBNode<E extends Comparable<E>> extends BSTNode<E>

    {

static final boolean RED=false;
        static final boolean BLACK=true;
        
        
        
        RBNode<E> parent;
        boolean color;//red or black

        
        
        RBNode(RBNode<E> p,E key,boolean color) {
            super(key);
            this.color=color;
            this.parent=p;
            
        }
        
        
        
        final boolean isNull(){return key==null;}
        
    }
    
    
        
    @Override
    public final boolean delete(E ele) {
        RBNode<E> cur;
        
        int cmp;
        if(root==null)return false;
        cur=(RBNode<E>)root;
        while(!cur.isNull()&&(cmp=ele.compareTo(cur.key))!=0)
        {
            if(cmp<0)cur=(RBNode<E>)cur.left;
            else cur=(RBNode<E>)cur.right;
                
        }
        if(cur.isNull())
        {
            //can't find specified key

            return false;
        }
        if(!((RBNode<E>)cur.left).isNull()&&!((RBNode<E>)cur.right).isNull())
        {
            RBNode<E> prev=(RBNode<E>)cur.left;
        
            while(!((RBNode<E>)prev.right).isNull())
            {
                
                prev=(RBNode<E>)prev.right;
            }
            cur.key=prev.key;
            cur=prev;
        
        }
        if(!((RBNode<E>)cur.left).isNull())
        {
            if (cur == root) {
                root = cur.left;
                ((RBNode<E>)root).color=RBNode.BLACK;
                return true;
            }
            
            if(cur.parent.left==cur)
            {
                cur.parent.left=cur.left;
                ((RBNode<E>)cur.left).parent=cur.parent;
            }
            else

            {
                cur.parent.right
=cur.left;
                ((RBNode<E>)cur.left).parent=cur.parent;
            }
            if(cur.color==RBNode.BLACK)
            {
                ((RBNode<E>)cur.left).color=RBNode.BLACK;
            }
        }
        else if(!((RBNode<E>)cur.right).isNull())
        {
            if (cur == root) {
                root = cur.right;
                ((RBNode<E>)root).color=RBNode.BLACK;
                return true;
            }
            
            if(cur.parent.left==cur)
            {
                cur.parent.left=cur.right;
                ((RBNode<E>)cur.right).parent=cur.parent;
            }
            else

            {
                cur.parent.right
=cur.right;
                ((RBNode<E>)cur.right).parent=cur.parent;
            }
            if(cur.color==RBNode.BLACK)
            {
                ((RBNode<E>)cur.right).color=RBNode.BLACK;
            }
        }
        else

        {
            
if(cur==root)
            {
                root=null;
                return true;
            }
            RBNode<E> todo;
            if(cur.parent.left==cur)
            {
                todo=newNullNode(cur.parent);
                cur.parent.left=todo;
            }
            
            else

            {
                todo
=newNullNode(cur.parent);
                cur.parent.right=todo;
            }
            if(cur.color==RBNode.BLACK)
            {
                //now todo is a double black node, we will eliminate the double black

                fixup_double_black(todo);
            }
            
        }
        
        return true;
    }

@Override
    public E findMax() {
        if(isEmpty())return null;
        BSTNode<E> node=root;
        while(!((RBNode<E>)node.right).isNull())
        {
            node=node.right;
        }
        return node.key;
    }

@Override
    public E findMin() {
        if(isEmpty())return null;
        BSTNode<E> node=root;
        while(!((RBNode<E>)node.left).isNull())
        {
            node=node.left;
        }
        return node.key;
    }

private final RBNode<E> newNullNode(RBNode<E> p)
    {
        return new RBNode<E>(p,null,RBNode.BLACK);
    }
    
    private final RBNode<E> newNormalNode(RBNode<E> p,E key, boolean color)
    {
        RBNode<E> node= new RBNode<E>(p,key,color);
        node.left=newNullNode(node);
        node.right=newNullNode(node);
        return node;
    }

private final void fixup_double_black(RBNode<E> cur) {
        
        RBNode<E> sibling;
        RBNode<E> p;
    
        while(cur!=root)//until its parent,parent maybe double black 

        {
            p=cur.parent;
            if(p.left==cur)
            {
                sibling=(RBNode<E>)p.right;
                if(sibling.color==RBNode.RED)
                {
                    rotate_from_right(p);
                    p.color=RBNode.RED;
                    sibling.color=RBNode.BLACK;//actually, p.parent==sibling, remember we have done one rotation
                    //this case  transformed to another case handled in other place

                }
                else 
                {
                    if(((RBNode<E>)sibling.right).color==RBNode.RED)
                    {
                        rotate_from_right(p);
                        sibling.color=p.color;//also, p.parent==sibling, some textbook say here sibling's color can be red while not violate the 3th rule, i don't think so.

                        p.color=RBNode.BLACK;
                        ((RBNode<E>)sibling.right).color=RBNode.BLACK;
                        //ok done!

                        return;
                        
                    }
                    else if(((RBNode<E>)sibling.left).color==RBNode.RED)
                    {
                        rotate_from_left(sibling);
                        sibling.color=RBNode.RED;
                        sibling.parent.color=RBNode.BLACK;//its parent was previously be its left child, remember we have done a left rotation from sibling
                        
                        // now transformed to previous case, double black 's sibling(black) have right child colored red

                        
                    }
                    else //  sibling's two children are both black

                    {
                        //re-coloring the sibling and parent

                        sibling.color=RBNode.RED;
                        if(p.color==RBNode.BLACK)
                        {
                            cur=p;
                            //now the cur node was not double black node ,but p was a double black

                        }
                        else

                        {
                            p.color
=RBNode.BLACK;//eliminated the double black node 
                            return;
                        }
                    }
                }
            }
            else

            {
                sibling
=(RBNode<E>)p.left;
                if(sibling.color==RBNode.RED)
                {
                    rotate_from_left(p);
                    p.color=RBNode.RED;
                    sibling.color=RBNode.BLACK;//actually, p.parent==sibling, remember we have done one rotation
                    //this case  transformed to another case handled in other place

                }
                else 
                {
                    if(((RBNode<E>)sibling.left).color==RBNode.RED)
                    {
                        rotate_from_left(p);
                        sibling.color=p.color;//also, p.parent==sibling, some textbook say here sibling's color can be red while not violate the 3th rule, i don't think so.

                        p.color=RBNode.BLACK;
                        ((RBNode<E>)sibling.left).color=RBNode.BLACK;
                        //ok done!

                        return;
                        
                    }
                    else if(((RBNode<E>)sibling.right).color==RBNode.RED)
                    {
                        rotate_from_right(sibling);
                        sibling.color=RBNode.RED;
                        sibling.parent.color=RBNode.BLACK;//its parent was previously be its right child, remember we have done a left rotation from sibling
                    
                        // now transformed to previous case, double black 's sibling(black) have right child colored red

                        
                    }
                    else //  sibling's two children are both black

                    {
                        //re-coloring the sibling and parent

                        sibling.color=RBNode.RED;
                        if(p.color==RBNode.BLACK)
                        {
                            cur=p;
                            //now the cur node was not double black node ,but p was a double black

                        }
                        else

                        {
                            p.color
=RBNode.BLACK;//eliminated the double black node 
                            return;
                        }
                    }
                }
            }
        }
        
    }

@Override
    public final void insert(E ele) {
        if(root==null)
        {
            root=newNormalNode(null,ele,RBNode.BLACK);//now root's black-height(bh) is 1

            return;
        }
        RBNode<E> ret=_insert((RBNode<E>)root,ele);//first insert it

        _insert_fixup(ret);//fix-up the R-B tree from node cur;
    }
    
    
    private final void _insert_fixup(RBNode<E> cur) {
        
        RBNode<E> p,g;
    
        //we should fix until it is black colored

        while(cur!=root&&cur.color==RBNode.RED)
        {
            p=cur.parent;
            if(p.color==RBNode.BLACK)
            {
                //that's fine, the insertion will not change any black height, and will not violate any rule.

                return;
            }
            g=p.parent;//we can assume the p is not null now.

            
            if(p==g.left)
            {
                RBNode<E> uncle=(RBNode<E> )g.right;
                if(uncle.color==RBNode.RED)
                {
                    //re-coloring

                    g.color=RBNode.RED;
                    uncle.color=p.color=RBNode.BLACK;
                    
                    //now the g maybe conflict with its parent;

                    cur=g;
                }
                else

                {
                    
if(cur==p.right)
                    {
                        //this case we should do left rotation, and then it will transform to next case

                        cur=rotate_from_right(p);
                        cur=(RBNode<E>)cur.left;
                        //transformed to next case    

                    }
                    
                    cur=rotate_from_left(g);
                    cur.color=RBNode.BLACK;
                    ((RBNode<E>)cur.left).color=((RBNode<E>)cur.right).color=RBNode.RED;
                                    
                
                }
                
            }
            else

            {
                RBNode
<E> uncle=(RBNode<E> )g.left;
                if(uncle.color==RBNode.RED)
                {
                    //re-coloring

                    g.color=RBNode.RED;
                    uncle.color=p.color=RBNode.BLACK;
                    
                    //now the g maybe conflict with its parent;

                    cur=g;
                }
                else

                {
                    
if(cur==p.left)
                    {
                        //this case we should do right rotation, and then it will transform to next case

                        cur=rotate_from_left(p);
                        cur=(RBNode<E>)cur.right;
                        //transformed to next case    

                    }
                    
                    cur=rotate_from_right(g);
                    cur.color=RBNode.BLACK;
                    ((RBNode<E>)cur.left).color=((RBNode<E>)cur.right).color=RBNode.RED;
                                    
                
                }
            }
                
        }
        ((RBNode<E>)root).color=RBNode.BLACK;
        
        
    }

private final RBNode<E> rotate_from_right(RBNode<E> p) {
        
        RBNode<E> g=p.parent;
        RBNode<E> cur=(RBNode<E>)p.right;
        
        p.right=cur.left;
        if(cur.left!=null)((RBNode<E>)cur.left).parent=p;
        
        cur.left=p;
        p.parent=cur;
        
        
        if(g!=null)
        {
            if(g.left==p)g.left=cur;
            else g.right=cur;
        }
        else root=cur;
        
        cur.parent=g;
        return cur;
        
    
    }

private final RBNode<E> rotate_from_left(RBNode<E> p) {
        RBNode<E> g=p.parent;
        RBNode<E> cur=(RBNode<E>)p.left;
        
        p.left=cur.right;
        if(cur.right!=null)((RBNode<E>)cur.right).parent=p;
        
        cur.right=p;
        p.parent=cur;
        
        
        if(g!=null)
        {
            if(g.left==p)g.left=cur;
            else g.right=cur;
        }
        else root=cur;
        
        cur.parent=g;
        return cur;
    }

private final RBNode<E> _insert(RBNode<E> cur,E ele)
    {
        
        RBNode<E> parent=null;
        int cmp;
        boolean left=false;
        while(!cur.isNull()&&(cmp=ele.compareTo(cur.key))!=0)
        {
            parent=cur;
            if(cmp<0)
            {
                cur=(RBNode<E>)cur.left;
                left=true;
                
            }
            else {cur=(RBNode<E>)cur.right;left=false;}
            
        }
        if(!cur.isNull())throw new IllegalArgumentException("can't insert duplicate key!");
        cur=newNormalNode(parent,ele,RBNode.RED);
        if(left)parent.left=cur;
        else parent.right=cur;
        return cur;
    }

/**
     * 
     
*/
    public RBTree() {
            root=null
;
    }

}

红黑树的java实现相关推荐

  1. 自己手写HashMap——红黑树的Java实现

    0.引言 (1)HashMap简单介绍 你好,这篇文章是<自己手写HashMap>的第一篇. 在java7之前,HashMap是用数组(hash桶)+链表的形式实现的,大概的原理就是对ke ...

  2. 红黑树在java中的作用_带你真正理解Java数据结构中的红黑树

    红黑树是平衡的二叉树,它不是一个完美的平衡二叉树,但是在动态插入过程中平衡二叉搜索树的代价相对较高,所以红黑树就此出现,下面就让爱站技术频道小编带你一起进入下文了解一下吧! 一.红黑树所处数据结构的位 ...

  3. 【数据结构与算法】红黑树的Java实现

    RedBlackTree原理分析 红黑树原理分析 RedBlackTree功能介绍 void insert(x) → Insert x void remove(x) → Remove x (unimp ...

  4. 《数据结构与算法之红黑树(Java实现)》

    说在前头:本人为大二在读学生,书写文章的目的是为了对自己掌握的知识和技术进行一定的记录,同时乐于与大家一起分享,因本人资历尚浅,发布的文章难免存在一些错漏之处,还请阅读此文章的大牛们见谅与斧正.若在阅 ...

  5. 红黑树 原理和算法详细介绍(Java)

    R-B Tree简介 R-B Tree,全称是Red-Black Tree,又称为"红黑树",它一种特殊的二叉查找树.红黑树的每个节点上都有存储位表示节点的颜色,可以是红(Red) ...

  6. java 二叉树 红黑树_常见数据结构(二)-树(二叉树,红黑树,B树)

    常见数据结构(二)-树(二叉树,红黑树,B树) 标签: algorithms [TOC] 本文介绍数据结构中几种常见的树:二分查找树,2-3树,红黑树,B树 写在前面 本文所有图片均截图自course ...

  7. 算法导论之红黑树的学习

    最近学习了二叉搜索树中的红黑树,感觉收获颇丰,在此写一篇文章小结一下学到的知识,顺便手写一下Java代码. 1.引言 先来讲讲什么是二叉搜索树,二叉搜索树有如下特点:他是以一颗二叉树(最多有两个子结点 ...

  8. 《漫画算法2》源码整理-1 二分查找树 AVL树 红黑树

    二分查找树 public class BinarySearchTree {private Node root;//查找结点public Node search(int data) {Node targ ...

  9. JDK源码学习笔记——TreeMap及红黑树

    找了几个分析比较到位的,不再重复写了-- Java 集合系列12之 TreeMap详细介绍(源码解析)和使用示例 [Java集合源码剖析]TreeMap源码剖析 java源码分析之TreeMap基础篇 ...

最新文章

  1. hibernate中hibernate.hbm2ddl.auto配置讲解
  2. [转] 不要被C++“自动生成”所蒙骗
  3. 搬家到新地址:http://kittsoft.xp3.biz/,欢迎访问!
  4. 10种常用降维算法源代码(python)
  5. Docker创建Dockerfile脚本构建jdk1.8镜像并启动容器示例
  6. python 排序统计滤波器_马尔可夫链+贝叶斯滤波器的Python展示
  7. Cannot resolve the collation conflict between SQL_Latin1_General_CP1_CI_AS and Latin1_General_100...
  8. Linux网络编程——tcp并发服务器(poll实现)
  9. 快手极速版自动评论脚本
  10. USB转串口CH340接线方法
  11. HC-05与JDY-09蓝牙模块对比与使用
  12. MOT任务中JDE(Jointly learns the Detector and Embedding model)算法解读
  13. 初识AMD型号CPU
  14. 获取字符串被分割后的总数组长度 java 类似UBound()方法
  15. Jquery实现大于等于且小于等于-遁地龙卷风
  16. MySQL的几个图形化界面工具
  17. bp神经网络模式识别,bp神经网络数字识别
  18. 豆瓣8.0分,尺度堪比色戒,一部让人绝望的电影
  19. 什么是混合移动App开发?
  20. 华为网络工程师项目模拟

热门文章

  1. 大型专利公司岗位设置
  2. python#原创第13篇~while循环+答案
  3. java view template_Java设计模式之模板方法模式(Template Method)
  4. 是谁扼杀了员工的敬业度?[转]
  5. 亿纬锂能:公司被选定为博世的供应商 为博世提供锂离子动力电池
  6. 中国移动将于11月4日首发上会 拟募资560亿元
  7. OPPO K9s官宣:5000mAh超大电量 充电功率阉割明显
  8. 爱奇艺取消剧集超前点播
  9. realme下月将推Q3s:Q系列下半年独苗 配置更强更全面
  10. 千万别让海底捞知道你的生日