紅黑樹(3)殘疾版紅黑樹新增實現
阿新 • • 發佈:2018-12-26
為什麼說是殘疾版呢,因為標準的紅黑樹是是三個值在一層,也就是父節點的左右分支節點都可以是紅,但在此,我規定了只有左分支為紅,也就是規定了最多隻有兩個值在一層。這樣能減少很多修復平衡判斷條件。在此我以實現簡化版的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());
}
}
}