1. 程式人生 > >紅黑樹(3)殘疾版紅黑樹新增實現

紅黑樹(3)殘疾版紅黑樹新增實現

為什麼說是殘疾版呢,因為標準的紅黑樹是是三個值在一層,也就是父節點的左右分支節點都可以是紅,但在此,我規定了只有左分支為紅,也就是規定了最多隻有兩個值在一層。這樣能減少很多修復平衡判斷條件。在此我以實現簡化版的treeMap 為例。

新增節點的第一步就是找出節點將要加入的位置,然後才修復平衡。

節點類的定義:

package hlm.com.treemap;

/**
* 節點:
* value 存值
* left 左子節點
* right 右子節點
* father 父節點(父節點為空,那表明該節點是根節點)
* color 顏色,
*/
public class Node {

private Object key ;
private Object value ;
private boolean color ;
private Node left ;
private Node right ;
private Node parent ;

public static final boolean BLANK = false ;
public static final boolean RED = true ;
public static final boolean LEFT = false ;
public static final boolean RIGHT = true ;

public Node() { }

public Node(Object key, Object value) {
this.key = key;
this.value = value;
this.color = BLANK ;
}

public Node(Object key, boolean color, Node left, Node right, Node parent) {
this.key = key;
this.color = color;
this.left = left;
this.right = right;
this.parent = parent;
}

public Object getValue() {
return value;
}

public void setValue(Object value) {
this.value = value;
}

public Node(Object key) {
this.key = key;
}

public Object getKey() {
return key;
}

public void setKey(Object key) {
this.key = key;
}

public boolean getColor() {
return color;
}

public void setColor(boolean color) {
this.color = color;
}

public Node getLeft() {
return left;
}

public void setLeft(Node left) {
this.left = left;
}

public Node getRight() {
return right;
}

public void setRight(Node right) {
this.right = right;
}

public Node getParent() {
return parent;
}

public void setParent(Node parent) {
this.parent = parent;
}

@Override
public String toString(){
return "k:"+ this.key+";v:"+this.value;
}
}

TreeMap 類的其他資訊

public class MyTreeMap {

Logger log = LoggerFactory.getLogger(MyTreeMap.class) ;

/*** 根節點*/
private Node root ;

/*** 元素個數*/
private transient int size ;

}

入口 put 方法

/**
     * 新增k-v方法
     * @param k
     * @param v
     * @return
     */
    public Object put(Object k ,Object v){

        Node node = new Node(k , v);

        //沒有根那麼傳進來的就是根
        if(root == null){
            root = node ;
            size ++ ;
            return root.getKey();
        }

       return addRedBlankNode(root , node);
    }

插入節點方法 addRedBlankNode

 /**
     * 按紅黑樹的方式新增節點
     * @param root
     * @param node
     * @return
     */
    private Object addRedBlankNode(Node root ,Node node){
        //起點不存在,那麼返回null
        if(root == null ){
            return null ;
        }
        //插入節點為空,那無法插入
        if(node == null ){
            return null ;
        }

        Object targetKey = node.getKey();
        int tarint = hash(targetKey);
        Object rootKey = root.getKey();
        int rootint = hash(rootKey);
        //相等,剛覆蓋更新
        if(tarint == rootint){
            root.setValue(node.getValue());
            return node.getValue();
        }
        //小於,左分支
        if(tarint < rootint){
            //存在左子節點時,遞迴檢索進去
            if(root.getLeft() != null){
                return addRedBlankNode(root.getLeft() , node);
            }else{
                root.setLeft(node);
                node.setParent(root);

            }
        }

        //大於,右分支(由於只構建2-3樹,所以新增右分支)
        if(tarint > rootint){
            //存在右子節點時,遞迴檢索進去
            if(root.getRight() != null){
                return addRedBlankNode(root.getRight() , node);
            }else{
                root.setRight(node);
                node.setParent(root);
            }
        }

        //修復平衡
        fixBalance(node);
        size ++ ;
        return node.getKey();

    }

插入節點其實很簡單,一路判斷下去直到有位置即可

最後是修復平衡方法 fixBalance 。這個才是主角。

/**
     * 調整樹結構及按需重新著色
     * @param node
     */
    private void fixBalance(Node node){
        //第一種情況,到頂了,node就是根了,那麼要考慮把node的顏色變黑,
        if(node.getParent() == null){
            node.setColor(Node.BLANK);
        }
        //左支
       else if(Node.LEFT == checkLR(node,node.getParent())){
            //父節點為黑時,如果無左子節點或左子節點為黑,直接顏色置為紅
            if(Node.BLANK == node.getParent().getColor() && (
                    (node.getLeft() !=null && node.getLeft().getColor()==Node.BLANK)
                            ||node.getLeft() == null)){
                node.setColor(Node.RED);
            }
            //父節點為黑時,但其左子節點為紅,由於不能連續兩紅
            else if(Node.BLANK == node.getParent().getColor() &&
                    node.getLeft() !=null && node.getLeft().getColor()==Node.RED ){
                node.getLeft().setColor(Node.BLANK);
                turnRight(node);
                fixBalance(node);

            }
            //父節點為紅時,其祖父節點是一定存在的
            //此時父節點那一層肯定是滿的,只有進行變換了,看情況如何變換
            else if(Node.RED == node.getParent().getColor()){
                    Node root = node.getParent();
                    root.setColor(Node.BLANK);
                    turnRight(root);
                    fixBalance(root);
            }


        }

        //右支 (想辦法變為左支)
        else if(Node.RIGHT == checkLR(node,node.getParent())){
            Node root = node.getParent();
            if(node.getLeft()==null ||node.getLeft().getColor() == Node.BLANK){
                if(root.getColor() == Node.RED){
                    turnLeft(node);
                    fixBalance(node);
                }else{
                    turnLeft(node);
                    fixBalance(root);
                }

            }
             else{
                node.getLeft().setColor(Node.BLANK);
                turnRight(node.getLeft());
                turnLeft(node.getLeft());
                fixBalance(node.getLeft());

            }
        }

    }