查找(二):彻底理解红黑树和平衡查找树

平衡查找树

在之前的二分搜索和二叉查找树中已经能够很好地解决查找的问题了,但是它们在最坏情况下的性能还是很糟糕,我们可以在查找二叉树中,每次动态插入或删除某结点时,都重新构造为完全二叉树,但是这样代价太大,所以就引出了平衡查找树。

详细的数学定义就不给出了,因为既不直观也记不住,直接给出一个平衡二叉树的图:

相信这个图一看就明白了,平衡查找树(以下简称BST或2-3查找树),下面给出一些基本的定义:

一棵2-3查找树或为一棵空树,或由一下结点组成:

2-结点,含有一个键(及其对应的值)和两条链接,左链接指向的2-3树中的键都小于该结点,右链接指向的2-3树中的键都大于该结点。

3-结点,含有两个键(及其对应的值)和三条链接,左链接指向的2-3树中的键都小于该结点,中链接指向的2-3树中的键都位于该结点的两个键之间,右链接指向的2-3树中的键都大于该结点。

因为平衡二叉树在插入和删除过程中需要判断插入的结点时2-结点还是3-结点等等一系列问题,实现起来代码量特别大,并且会增加额外开销,所以就提出了红黑树。

红黑树

红黑树的基本思想是用标准的二叉查找树(完全由2-结点构成)和一些额外的信息(替换3-结点)来表示2-3树。

先给图:

由上图可以很明显的看到红黑树可以完全代替2-3树,下面给出红黑树的定义:

  1. 红连接均为左链接。
  2. 没有任何一个结点同时和两条红链接相连
  3. 该树是完美黑色平衡的,即任意空链接到根节点的路径上的黑链接的数量相同

先给出红黑树Node的定义:

    private class Node<T> {
        T key;
        Node leftChild = null;
        Node rightChild = null;
        boolean color;

        Node(T key,boolean color) {
            this.key = key;
            this.color = color;
        }
    }

在Node类中,相比普通的二叉树,只是多了一个color属性,因为对于每一个非根节点,有且仅有一个链接指向它,所以这个color用来表示指向这个节点的链接是红色还是黑色,也可以理解为此节点是红色或黑色。

然后再给出两个操作:左旋转和右旋转以及颜色转换。

这三个操作都是局部改变,并不会对整个红黑树造成破坏

左旋转:

对于这个情况,h的右链接为红色,不符合红黑树的定义

操作的代码如下:

    public <T> Node<T> rotateLeft(Node<T> h) {
        Node<T> x = h.rightChild;
        h.rightChild = x.leftChild;
        x.leftChild = h;
        x.color = h.color;
        h.color = RED;

        return x;
    }

同理右旋转也就是将左边的红色链接换到右边:

    public <T> Node<T> rotateRight(Node<T> h) {
        Node<T> x = h.leftChild;
        h.leftChild = x.rightChild;
        x.rightChild = h;
        x.color = h.color;
        h.color = RED;

        return x;
    }

颜色转换:

实现代码

    public <T> void flipColors(Node<T> h) {
        h.color = RED;
        h.leftChild.color = BLACK;
        h.rightChild.color = BLACK;
    }

这三种操作说完了,那么我们为什么需要这三个操作呢? 其实数学原理特别复杂,如果大家有兴趣可自行GOOGLE。

答案是这三个操作时用来构建一个红黑树的,当插入一个元素到红黑树中时,需要沿着插入点到根节点的路径上向上移动,对于经过的每个结点,有如下操作:

  1. 如果右子节点是红色的而左子节点也是红色的,进行右旋转。
  2. 如果左子节点是红色的且它的左子节点也是红色的,进行右旋转
  3. 如果左右子节点均为红色,进行颜色转换。

这样的话只需要在二叉查找树上稍作修改即可,完整代码如下:

public class RedBlackTree extends SearchBase {

    private static final boolean RED = true;
    private static final boolean BLACK = false;

    @SuppressWarnings("unused")
    private class Node<T> {
        T key;
        Node leftChild = null;
        Node rightChild = null;
        boolean color;

        Node(T key,boolean color) {
            this.key = key;
            this.color = color;
        }
    }

    /* (non-Javadoc)
     * @see Search.SearchBase#search(java.lang.Comparable)
     */
    @Override
    public <T> Integer search(Comparable<T> key) {
        // TODO Auto-generated method stub
        return null;
    }

    @SuppressWarnings("unchecked")
    public <T> T search(Comparable<T> key,Node<T> root) {
        // TODO Auto-generated method stub
        Node<T> node = root;
        while(node != null) {
            if(key.compareTo((T) node.key) < 0) {
                node = node.leftChild;
            } else if(key.compareTo((T) node.key) > 0){
                node = node.rightChild;
            } else {
                break;
            }
        }

        if(node == null)
            return null;
        else
            return (T) node.key;
    }

    public <T> Node<T> rotateLeft(Node<T> h) {
        Node<T> x = h.rightChild;
        h.rightChild = x.leftChild;
        x.leftChild = h;
        x.color = h.color;
        h.color = RED;

        return x;
    }

    public <T> Node<T> rotateRight(Node<T> h) {
        Node<T> x = h.leftChild;
        h.leftChild = x.rightChild;
        x.rightChild = h;
        x.color = h.color;
        h.color = RED;

        return x;
    }

    public <T> void flipColors(Node<T> h) {
        h.color = RED;
        h.leftChild.color = BLACK;
        h.rightChild.color = BLACK;
    }

    public <T> boolean isRed(Node<T> node) {
        if(node == null)
            return false;
        return node.color == RED;
    }

    @SuppressWarnings("unchecked")
    public <T> Node<T> put(Node<T> node,Comparable<T> key) {
        if(node == null)
            return new Node(key,RED);

        if(key.compareTo((T) node.key) < 0)
            node.leftChild = put(node.leftChild,key);
        else if(key.compareTo((T) node.key) > 0)
            node.rightChild = put(node.rightChild,key);
        else
            node.key = (T) key;

        if(isRed(node.rightChild) && !isRed(node.leftChild))
            node = rotateLeft(node);
        if(isRed(node.leftChild) && isRed(node.leftChild.leftChild))
            node = rotateRight(node);
        if(isRed(node) && isRed(node.rightChild))
            flipColors(node);

        return node;
    }

    public <T> void traverseTree(Node<T> node) {
        if(node == null)
            return;

        traverseTree(node.leftChild);
        System.out.print(node.key);
        traverseTree(node.rightChild);
    }

    public static <T> void main(String[] args) {
        Integer[] b = {1,4,2,6,7,0,3};
        RedBlackTree redBlackTree = new RedBlackTree();
        RedBlackTree.Node<Integer> root = null;

        for(int i=0;i<b.length;i++) {
            root = redBlackTree.put(root, b[i]);
        }

        redBlackTree.traverseTree(root);

        System.out.println();
        Integer key = redBlackTree.search(8, root);
        System.out.println(key);
    }
}

红黑树平均插入效率:lgN

红黑树平均查询效率:lgN

时间: 2024-11-09 13:55:14

查找(二):彻底理解红黑树和平衡查找树的相关文章

浅谈算法和数据结构: 七 二叉查找树 八 平衡查找树之2-3树 九 平衡查找树之红黑树 十 平衡查找树之B树

http://www.cnblogs.com/yangecnu/p/Introduce-Binary-Search-Tree.html 前文介绍了符号表的两种实现,无序链表和有序数组,无序链表在插入的时候具有较高的灵活性,而有序数组在查找时具有较高的效率,本文介绍的二叉查找树(Binary Search Tree,BST)这一数据结构综合了以上两种数据结构的优点. 二叉查找树具有很高的灵活性,对其优化可以生成平衡二叉树,红黑树等高效的查找和插入数据结构,后文会一一介绍. 一 定义 二叉查找树(B

30张图带你彻底理解红黑树

本文转自安卓大叔 写在前面 当在10亿数据中只需要进行10几次比较就能查找到目标时,不禁感叹编程之魅力!人类之伟大呀! —— 学红黑树有感. 终于,在学习了几天的红黑树相关的知识后,我想把我所学所想和所感分享给大家.红黑树是一种比较难的数据结构,要完全搞懂非常耗时耗力,红黑树怎么自平衡?什么时候需要左旋或右旋?插入和删除破坏了树的平衡后怎么处理?等等一连串的问题在学习前困扰着我.如果你在学习过程中也会存在我的疑问,那么本文对你会有帮助,本文帮助你全面.彻底地理解红黑树! 本文将通过图文的方式讲解

通过2-3-4树理解红黑树

前言 红黑树是数据结构中比较复杂的一种,最近与它交集颇多,于是花了一周的空闲时间跟它死磕,终于弄明白并实现了红黑树.写文总结一下,希望能给试图理解红黑树的同学一些灵感,也让我能记得更深刻. 在研究红黑树时吃了不少苦头,原因有二: 红黑树的插入和删除非常复杂,很多人并没有理解或完全实现,或实现了的没有任何注释,让人很难参考: 网络上红黑树的理解方式较为单一,一般是 双黑.caseN 法,而插入和删除的情况很多,每种都有对应的处理方式,如果死记硬背的话,再过一段时间再回忆各种情况可能就一头雾水了.

通过2-3树理解红黑树

一.简介 前面的文章我们循序渐进的讲解了<二叉树><二分搜索树><AVL-平衡二叉树>,从左至右互为基础.尤其是二分搜索树给了我们如何将数据组织成为搜索树的思想,当然二分搜索树存在的天然问题--在极端情况下回退化为链表.所以引出了AVL-平衡二叉树,通过再平衡即LL,LR,RR,RL四个旋转操作维护了一棵平衡的二分搜索树.本章节我们继续梳理一个高阶的树结构即:红黑树.想必大家都知道,红黑树如何维持平衡,如何进行颜色反转让人很难理解,虽然很多博文很多书对红黑树都有讲解,但

二叉树,平衡树,红黑树,B~/B+树汇总

二叉查找树(BST),平衡二叉查找树(AVL),红黑树(RBT),B~/B+树(B-tree).这四种树都具备下面几个优势: (1) 都是动态结构.在删除,插入操作的时候,都不需要彻底重建原始的索引树.最多就是执行一定量的旋转,变色操作来有限的改变树的形态.而这些操作所付出的代价都远远小于重建一棵树.这一优势在<查找结构专题(1):静态查找结构概论 >中讲到过. (2) 查找的时间复杂度大体维持在O(log(N))数量级上.可能有些结构在最差的情况下效率将会下降很快,比如二叉树 1.二叉查找树

平衡搜索树(二) Rb 红黑树

Rb树简介 红黑树是一棵二叉搜索树,它在每个节点上增加了一个存储位来表示节点的颜色,可以是Red或Black.通过对任何一条从根到叶子简单 路径上的颜色来约束,红黑树保证最长路径不超过最短路径的两倍,因而近似于平衡.(性质3.性质4保证了红黑树最长的路径不超过最短路径的两倍) 如图所示: 红黑树是满足下面红黑性质的二叉搜索树 1. 每个节点,不是红色就是黑色的 2. 根节点是黑色的 3. 如果一个节点是红色的,则它的两个子节点是黑色的 4. 对每个节点,从该节点到其所有后代叶节点的简单路径上,均

深入理解红黑树及C++实现

介绍 红黑树是一种特殊的平衡二叉树(AVL),可以保证在最坏的情况下,基本动态集合操作的时间复杂度为O(logn).因此,被广泛应用于企业级的开发中. 红黑树的性质 在一棵红黑树中,其每个结点上增加了一个存储位(属性color)来表示结点的颜色,且颜色只能是red or black.通过对任何一条从根到叶子的简单路径上各个结点的颜色进行约束,红黑树确保没有一条路径会比其他路径长出2倍,因而是近似于平衡的. 树中每个结点包含5个属性:color.val.lchild.rchild和p(可选).如果

深入理解红黑树

红黑树是平衡树的一种,保证最坏情况下操作时间复杂度为O(lgo(n)).红黑树的应用比较广泛,比如作为C++中STL的set和map的底层数据结构,Java集合中TreeSet和TreeMap的底层数据结构等.学习红黑树,可以把二叉查找树作为参考,这样有助于加深理解.红黑树的操作主要包括节点旋转.插入.删除等操作,下面咱们就一一来看: 1.红黑树性质 每个节点是红色的,或者是黑色的 根节点是黑色的 每个叶节点(nil)是黑色的 如果一个节点是红色的,则它的两个子节点都是黑色的 对每个节点,从该节

结合java.util.TreeMap源码理解红黑树

前言 本篇将结合JDK1.6的TreeMap源码,来一起探索红-黑树的奥秘.红黑树是解决二叉搜索树的非平衡问题. 当插入(或者删除)一个新节点时,为了使树保持平衡,必须遵循一定的规则,这个规则就是红-黑规则: 1) 每个节点不是红色的就是黑色的 2) 根总是黑色的 3) 如果节点是红色的,则它的子节点必须是黑色的(反之倒不一定必须为真) 4) 从跟到叶节点或者空子节点的每条路径,必须包含相同数目的黑色节点 插入一个新节点 红-黑树的插入过程和普通的二叉搜索树基本一致:从跟朝插入点位置走,在每个节