在以前的二分搜索和二叉查找樹中已經可以很好地解決查找的問題了,可是它們在最壞狀況下的性能仍是很糟糕,咱們能夠在查找二叉樹中,每次動態插入或刪除某結點時,都從新構造爲徹底二叉樹,可是這樣代價太大,因此就引出了平衡查找樹。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樹,下面給出紅黑樹的定義:數學
先給出紅黑樹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。
答案是這三個操做時用來構建一個紅黑樹的,當插入一個元素到紅黑樹中時,須要沿着插入點到根節點的路徑上向上移動,對於通過的每一個結點,有以下操做:
這樣的話只須要在二叉查找樹上稍做修改便可,完整代碼以下:
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