手撕紅黑樹
紅黑樹的解析請參考博文
http://www.lxweimin.com/p/e136ec79235c
作者寫的十分詳盡!
注釋版源碼(代碼來自美團技術團隊)
package collection;
public class RBTreeNode<T extends Comparable<T>> {
private T value;//node value
private RBTreeNode<T> left;//left child pointer
private RBTreeNode<T> right;//right child pointer
private RBTreeNode<T> parent;//parent pointer
private boolean red;//color is red or not red
public RBTreeNode(){}
public RBTreeNode(T value){this.value=value;}
public RBTreeNode(T value,boolean isRed){this.value=value;this.red = isRed;}
public T getValue() {
return value;
}
void setValue(T value) {
this.value = value;
}
RBTreeNode<T> getLeft() {
return left;
}
void setLeft(RBTreeNode<T> left) {
this.left = left;
}
RBTreeNode<T> getRight() {
return right;
}
void setRight(RBTreeNode<T> right) {
this.right = right;
}
RBTreeNode<T> getParent() {
return parent;
}
void setParent(RBTreeNode<T> parent) {
this.parent = parent;
}
boolean isRed() {
return red;
}
boolean isBlack(){
return !red;
}
/**
* is leaf node
**/
boolean isLeaf(){
return left==null && right==null;
}
void setRed(boolean red) {
this.red = red;
}
void makeRed(){
red=true;
}
void makeBlack(){
red=false;
}
@Override
public String toString(){
return value.toString();
}
}
package collection;
import collection.RBTreeNode;
import sun.security.mscapi.CPublicKey;
public class RBTree<T extends Comparable<T>> {
private final RBTreeNode<T> root;
//node number
private java.util.concurrent.atomic.AtomicLong size =
new java.util.concurrent.atomic.AtomicLong(0);
//in overwrite mode,all node's value can not has same value
//in non-overwrite mode,node can have same value, suggest don't use non-overwrite mode.
private volatile boolean overrideMode=true;
public RBTree(){
this.root = new RBTreeNode<T>();
}
public RBTree(boolean overrideMode){
this();
this.overrideMode=overrideMode;
}
public boolean isOverrideMode() {
return overrideMode;
}
public void setOverrideMode(boolean overrideMode) {
this.overrideMode = overrideMode;
}
/**
* number of tree number
* @return
*/
public long getSize() {
return size.get();
}
/**
* get the root node
* @return
*/
private RBTreeNode<T> getRoot(){
return root.getLeft();
}
/**
* add value to a new node,if this value exist in this tree,
* if value exist,it will return the exist value.otherwise return null
* if override mode is true,if value exist in the tree,
* it will override the old value in the tree
*
* @param value
* @return
*/
public T addNode(T value){
RBTreeNode<T> t = new RBTreeNode<T>(value);
return addNode(t);
}
/**
* find the value by give value(include key,key used for search,
* other field is not used,@see compare method).if this value not exist return null
* @param value
* @return
*/
public T find(T value){
RBTreeNode<T> dataRoot = getRoot();
while(dataRoot!=null){
int cmp = dataRoot.getValue().compareTo(value);
if(cmp<0){
dataRoot = dataRoot.getRight();
}else if(cmp>0){
dataRoot = dataRoot.getLeft();
}else{
return dataRoot.getValue();
}
}
return null;
}
/**
* remove the node by give value,if this value not exists in tree return null
* @param value include search key
* @return the value contain in the removed node
*/
public T remove(T value){
RBTreeNode<T> dataRoot = getRoot();
RBTreeNode<T> parent = root;
while(dataRoot!=null){
//尋找對應的目標節點
int cmp = dataRoot.getValue().compareTo(value);
if(cmp<0){
parent = dataRoot;
dataRoot = dataRoot.getRight();
}else if(cmp>0){
parent = dataRoot;
dataRoot = dataRoot.getLeft();
}else{
if(dataRoot.getRight()!=null){
//找到對應的目標節點的后繼節點
RBTreeNode<T> min = removeMin(dataRoot.getRight());
//x used for fix color balance
//等觀察后繼節點是否有右節點,如果有的話要在刪除后繼節點其實就是相當于刪除了其唯一的右節點,因此此時的替換節點為其右子節點
RBTreeNode<T> x = min.getRight()==null ? min.getParent() : min.getRight();
boolean isParent = min.getRight()==null;
//用后繼節點替換掉目標節點
min.setLeft(dataRoot.getLeft());
setParent(dataRoot.getLeft(),min);
//建立parent與后繼節點的聯系
if(parent.getLeft()==dataRoot){
parent.setLeft(min);
}else{
parent.setRight(min);
}
setParent(min,parent);
//后繼節點的顏色,紅色的話直接刪除,不影響紅黑樹的平衡,黑色的話需要調整平衡性
boolean curMinIsBlack = min.isBlack();
//inherit dataRoot's color
min.setRed(dataRoot.isRed());
if(min!=dataRoot.getRight()){
min.setRight(dataRoot.getRight());
setParent(dataRoot.getRight(),min);
}
//remove a black node,need fix color
//刪除黑色節點,需要進行修復
if(curMinIsBlack){
//如果后繼節點不是目標節點的右節點的話,進行修復
if(min!=dataRoot.getRight()){
fixRemove(x,isParent);
}else if(min.getRight()!=null){ //如果后繼節點還存在右子樹的話
//修復右子樹
fixRemove(min.getRight(),false);
}else{
fixRemove(min,true);
}
}
}else{
//當目標節點只有一個子節點時,用子節點替換掉目標節點就ok了
setParent(dataRoot.getLeft(),parent);
if(parent.getLeft()==dataRoot){
parent.setLeft(dataRoot.getLeft());
}else{
parent.setRight(dataRoot.getLeft());
}
//current node is black and tree is not empty
if(dataRoot.isBlack() && !(root.getLeft()==null)){
RBTreeNode<T> x = dataRoot.getLeft()==null
? parent :dataRoot.getLeft();
boolean isParent = dataRoot.getLeft()==null;
//進行修復
fixRemove(x,isParent);
}
}
setParent(dataRoot,null);
//吧dataRoot刪除了,gc
dataRoot.setLeft(null);
dataRoot.setRight(null);
if(getRoot()!=null){
getRoot().setRed(false);
getRoot().setParent(null);
}
//容量減一
size.decrementAndGet();
return dataRoot.getValue();
}
}
return null;
}
/**
* fix remove action
* @param node
* @param isParent
*/
private void fixRemove(RBTreeNode<T> node,boolean isParent){
RBTreeNode<T> cur = isParent ? null : node;
boolean isRed = isParent ? false : node.isRed();
RBTreeNode<T> parent = isParent ? node : node.getParent();
//迭代,直到遇到紅節點或者為根節點
while(!isRed && !isRoot(cur)){
//找到兄弟節點
RBTreeNode<T> sibling = getSibling(cur,parent);
//sibling is not null,due to before remove tree color is balance
//if cur is a left node
//后繼節點是否為左節點
boolean isLeft = parent.getRight()==sibling;
//case1:兄弟節點是紅節點且后繼節點為右子節點
//此時將s設置為黑色,p設置為紅色,p右旋,轉化為case2
if(sibling.isRed() && !isLeft){//case 1
//cur in right
parent.makeRed();
sibling.makeBlack();
rotateRight(parent);
}
//此時兄弟節點依然為紅節點,但替換節點為左子節點
//s設置為黑色,p設置為紅色,將p左旋,轉化為case2
else if(sibling.isRed() && isLeft){
//cur in left
parent.makeRed();
sibling.makeBlack();
rotateLeft(parent);
}
//case2:兄弟節點為黑色節點,且兄弟節點的左右節點都為黑,此時替換節點無法向兄弟節點借紅節點,只能借助父親節點重新設置,并且將s設置為紅節點
else if(isBlack(sibling.getLeft()) && isBlack(sibling.getRight())){//case 2
sibling.makeRed();
cur = parent;
isRed = cur.isRed();
parent=parent.getParent();
}
//case3:替換節點為左子節點且兄弟節點的左子節點為紅,右子節點為黑
//s設置成紅色,sl設置成黑色,s右旋
else if(isLeft && !isBlack(sibling.getLeft())
&& isBlack(sibling.getRight())){//case 3
sibling.makeRed();
sibling.getLeft().makeBlack();
rotateRight(sibling);
}
//替換節點為右子節點,兄弟節點的右子節點為紅,左子節點為黑,與上為鏡像
//s設置為紅,sr設置為黑,對s進行右旋
else if(!isLeft && !isBlack(sibling.getRight())
&& isBlack(sibling.getLeft()) ){
sibling.makeRed();
sibling.getRight().makeBlack();
rotateLeft(sibling);
}
//case4:替換節點在左,兄弟節點的右子節點為紅,左子節點任意顏色
//s置為p的顏色,p為黑色,sr為黑色,p左旋
else if(isLeft && !isBlack(sibling.getRight())){//case 4
sibling.setRed(parent.isRed());
parent.makeBlack();
sibling.getRight().makeBlack();
rotateLeft(parent);
cur=getRoot();
}
//與上鏡像
else if(!isLeft && !isBlack(sibling.getLeft())){
sibling.setRed(parent.isRed());
parent.makeBlack();
sibling.getLeft().makeBlack();
rotateRight(parent);
cur=getRoot();
}
}
//如果是紅色而退出迭代,那么將其設置為黑色,不影響
if(isRed){
cur.makeBlack();
}
if(getRoot()!=null){
getRoot().setRed(false);
getRoot().setParent(null);
}
}
//get sibling node
//獲取同級的節點,即兄弟節點
private RBTreeNode<T> getSibling(RBTreeNode<T> node,RBTreeNode<T> parent){
parent = node==null ? parent : node.getParent();
if(node==null){
return parent.getLeft()==null ? parent.getRight() : parent.getLeft();
}
if(node==parent.getLeft()){
return parent.getRight();
}else{
return parent.getLeft();
}
}
private boolean isBlack(RBTreeNode<T> node){
return node==null || node.isBlack();
}
private boolean isRoot(RBTreeNode<T> node){
return root.getLeft() == node && node.getParent()==null;
}
/**
* find the successor node
* 找到node的后繼節點
* @param node current node's right node
* @return
*/
private RBTreeNode<T> removeMin(RBTreeNode<T> node){
//find the min node
RBTreeNode<T> parent = node;
while(node!=null && node.getLeft()!=null){
parent = node;
node = node.getLeft();
}
//remove min node
if(parent==node){
return node;
}
parent.setLeft(node.getRight());
setParent(node.getRight(),parent);
//don't remove right pointer,it is used for fixed color balance
//node.setRight(null);
return node;
}
private T addNode(RBTreeNode<T> node){
//將node的左右和父親節點都置為null。顏色為red
node.setLeft(null);
node.setRight(null);
node.setRed(true);
setParent(node,null);
//如果當前樹是一顆空樹
if(root.getLeft()==null){
//以當前node作為根節點
root.setLeft(node);
//root node is black
//紅黑樹的根節點的顏色為黑
node.setRed(false);
//size加一
size.incrementAndGet();
}else{//如果當前樹不空
//找到node節點的父節點并返回
RBTreeNode<T> x = findParentNode(node);
//將父節點的值與node的值進行比較
int cmp = x.getValue().compareTo(node.getValue());
//如果相等的話,直接進行覆蓋就好了
if(this.overrideMode && cmp==0){
T v = x.getValue();
x.setValue(node.getValue());
return v;
}else if(cmp==0){//如果沒有開啟覆蓋模式,則返回原來的值,不進行覆蓋
//value exists,ignore this node
return x.getValue();
}
//如果不相等的話,設置node的父親節點
setParent(node,x);
if(cmp>0){
//如果node的值小于父節點的值,插入到左邊
x.setLeft(node);
}else{
x.setRight(node);
}
//修復插入,使之保持紅黑樹的平衡
fixInsert(node);
//size加一
size.incrementAndGet();
}
return null;
}
/**
* find the parent node to hold node x,if parent value equals x.value return parent.
* @param x
* @return
* 因為插入要插入到根節點,所以要找到節點對應的父節點
*/
private RBTreeNode<T> findParentNode(RBTreeNode<T> x){
RBTreeNode<T> dataRoot = getRoot();
RBTreeNode<T> child = dataRoot;
while(child!=null){
int cmp = child.getValue().compareTo(x.getValue());
if(cmp==0){
return child;
}
if(cmp>0){
dataRoot = child;
child = child.getLeft();
}else if(cmp<0){
dataRoot = child;
child = child.getRight();
}
}
return dataRoot;
}
/**
* red black tree insert fix.
* @param x
*/
private void fixInsert(RBTreeNode<T> x){
RBTreeNode<T> parent = x.getParent();//先找到父節點
//如果父親節點不為null且父節點是紅節點
//循環迭代,直到為根節點或者parent為黑色節點,如果parent為黑色節點,則紅黑樹不用再進行調整了
while(parent!=null && parent.isRed()){
//找到插入節點的叔叔節點
RBTreeNode<T> uncle = getUncle(x);
//如果叔叔節點為空,需要進行旋轉
if(uncle==null){//need to rotate
//得到祖父節點
RBTreeNode<T> ancestor = parent.getParent();
//ancestor is not null due to before before add,tree color is balance
if(parent == ancestor.getLeft()){
boolean isRight = x == parent.getRight();
//對應uncle不為紅,左右情況,需要先將p左旋,再將ancestor右旋
if(isRight){
rotateLeft(parent);
}
//左左,只需將ancestor右旋
rotateRight(ancestor);
//左右的話,x將作為局部根節點,設置為黑
if(isRight){
x.setRed(false);
parent=null;//end loop
}else{
//左左的話,parent為局部根節點,設置為黑
parent.setRed(false);
}
//無論那種情況,祖父節點都設置為紅
ancestor.setRed(true);
}else{
boolean isLeft = x == parent.getLeft();
//右左
if(isLeft){
rotateRight(parent);
}
rotateLeft(ancestor);
if(isLeft){
x.setRed(false);
parent=null;//end loop
}else{
parent.setRed(false);
}
ancestor.setRed(true);
}
}else{//uncle is red
parent.setRed(false);
uncle.setRed(false);
parent.getParent().setRed(true);
x=parent.getParent();
parent = x.getParent();
}
}
//根節點設置為黑色
getRoot().makeBlack();
getRoot().setParent(null);
}
/**
* get uncle node
* @param node
* @return
*/
private RBTreeNode<T> getUncle(RBTreeNode<T> node){
RBTreeNode<T> parent = node.getParent();
RBTreeNode<T> ancestor = parent.getParent();
if(ancestor==null){
return null;
}
if(parent == ancestor.getLeft()){
return ancestor.getRight();
}else{
return ancestor.getLeft();
}
}
private void rotateLeft(RBTreeNode<T> node){
//左旋必須要有right節點
RBTreeNode<T> right = node.getRight();
if(right==null){
throw new java.lang.IllegalStateException("right node is null");
}
RBTreeNode<T> parent = node.getParent();
node.setRight(right.getLeft());
setParent(right.getLeft(),node);
right.setLeft(node);
setParent(node,right);
if(parent==null){//node pointer to root
//right raise to root node
root.setLeft(right);
setParent(right,null);
}else{
if(parent.getLeft()==node){
parent.setLeft(right);
}else{
parent.setRight(right);
}
//right.setParent(parent);
setParent(right,parent);
}
}
private void rotateRight(RBTreeNode<T> node){
//右旋操作
RBTreeNode<T> left = node.getLeft();
if(left==null){
throw new java.lang.IllegalStateException("left node is null");
}
RBTreeNode<T> parent = node.getParent();
node.setLeft(left.getRight());
setParent(left.getRight(),node);
left.setRight(node);
setParent(node,left);
if(parent==null){
root.setLeft(left);
setParent(left,null);
}else{
if(parent.getLeft()==node){
parent.setLeft(left);
}else{
parent.setRight(left);
}
setParent(left,parent);
}
}
private void setParent(RBTreeNode<T> node,RBTreeNode<T> parent){
if(node!=null){
node.setParent(parent);
if(parent==root){
node.setParent(null);
}
}
}
/**
* debug method,it used print the given node and its children nodes,
* every layer output in one line
* @param root
*/
public void printTree(RBTreeNode<T> root){
java.util.LinkedList<RBTreeNode<T>> queue =new java.util.LinkedList<RBTreeNode<T>>();
java.util.LinkedList<RBTreeNode<T>> queue2 =new java.util.LinkedList<RBTreeNode<T>>();
if(root==null){
return ;
}
queue.add(root);
boolean firstQueue = true;
while(!queue.isEmpty() || !queue2.isEmpty()){
java.util.LinkedList<RBTreeNode<T>> q = firstQueue ? queue : queue2;
RBTreeNode<T> n = q.poll();
if(n!=null){
String pos = n.getParent()==null ? "" : ( n == n.getParent().getLeft()
? " LE" : " RI");
String pstr = n.getParent()==null ? "" : n.getParent().toString();
String cstr = n.isRed()?"R":"B";
cstr = n.getParent()==null ? cstr : cstr+" ";
System.out.print(n+"("+(cstr)+pstr+(pos)+")"+"\t");
if(n.getLeft()!=null){
(firstQueue ? queue2 : queue).add(n.getLeft());
}
if(n.getRight()!=null){
(firstQueue ? queue2 : queue).add(n.getRight());
}
}else{
System.out.println();
firstQueue = !firstQueue;
}
}
}
public static void main(String[] args) {
RBTree<String> bst = new RBTree<String>();
bst.addNode("d");
bst.addNode("d");
bst.addNode("c");
bst.addNode("c");
bst.addNode("b");
bst.addNode("f");
bst.addNode("a");
bst.addNode("e");
bst.addNode("g");
bst.addNode("h");
bst.remove("c");
bst.printTree(bst.getRoot());
}
}
難點
紅黑樹的查找效率十分的高,當在10億數據進行不到30次比較就能查找到目標時,不禁感嘆編程之魅力
其難點在于插入和刪除節點
復雜度
如果二叉排序樹是平衡的,則n個節點的二叉排序樹的高度為Log2n+1,其查找效率為O(Log2n),近似于折半查找。如果二叉排序樹完全不平衡,則其深度可達到n,查找效率為O(n),退化為順序查找。一般的,二叉排序樹的查找性能在O(Log2n)到O(n)之間。因此,為了獲得較好的查找性能,就要構造一棵平衡的二叉排序樹。 紅黑樹并不是一個完美平衡二叉查找樹,根結點的左子樹如果比右子樹高,但左子樹和右子樹的黑結點的層數是相等的,也即任意一個結點到到每個葉子結點的路徑都包含數量相同的黑結點。所以我們叫紅黑樹這種平衡為黑色完美平衡。 紅黑樹的主要目的是實現一種平衡二叉樹,這樣可以達到最優的查詢性能,時間復雜度為(O(logn)、 n為數據個數。 紅黑樹查找,因為紅黑樹是一顆二叉平衡樹,并且查找不會破壞樹的平衡,所以查找跟二叉平衡樹的查找無異。正由于紅黑樹總保持黑色完美平衡,所以它的查找最壞時間復雜度為O(2lgN),也即整顆樹剛好紅黑相隔的時候,能有這么好的查找效率得益于紅黑樹自平衡的特性。