查找(二):完全理解红黑树和平衡查找树

平衡查找树

在以前的二分搜索和二叉查找树中已经可以很好地解决查找的问题了,可是它们在最坏状况下的性能仍是很糟糕,咱们能够在查找二叉树中,每次动态插入或删除某结点时,都从新构造为彻底二叉树,可是这样代价太大,因此就引出了平衡查找树。java

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

 

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

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

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

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

 

 

 

由于平衡二叉树在插入和删除过程当中须要判断插入的结点时2-结点仍是3-结点等等一系列问题,实现起来代码量特别大,而且会增长额外开销,因此就提出了红黑树。code

 

红黑树

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

先给图:ip

 

由上图能够很明显的看到红黑树能够彻底代替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

相关文章
相关标签/搜索