查找(二):完全理解紅黑樹和平衡查找樹

平衡查找樹

在以前的二分搜索和二叉查找樹中已經可以很好地解決查找的問題了,可是它們在最壞狀況下的性能仍是很糟糕,咱們能夠在查找二叉樹中,每次動態插入或刪除某結點時,都從新構造爲徹底二叉樹,可是這樣代價太大,因此就引出了平衡查找樹。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

相關文章
相關標籤/搜索