一、封裝ADT
此類爲參考算法導論用java編寫。
package tree;
import java.util.concurrent.LinkedBlockingQueue;
/**
* 紅黑樹ADT(Abstract Data Struct)
* 本類純屬虛構,如有雷同純屬巧合。本類爲個人學習研究編寫,如有問題可指正。
*
*
* 紅黑樹是avl樹的一種,一般avl樹使用樹高和旋轉操作來保持平衡。類似的紅黑樹使用顏色和旋轉來保持平衡。
*
* 紅黑樹的插入比較簡單,在按照搜索樹插入一個節點後,將其染成紅色,由於可能與父節點形成連續紅色,所以需要簡單調整。將當前校對的節點稱爲基準節點。
* 1. 如果其叔節點爲空,在進行一次單旋即可。
* 1. 如果其叔節點爲紅色,則進行簡單的變色,就可以將基準節點上移。
* 2. 如果其叔節點爲黑色則可進行一次單旋,或者雙旋,相間改變顏色即可
*
* 紅黑樹的刪除比較複雜,首先按照搜索樹刪除一個節點(此過程本身已經很複雜)。然後將所有情況轉換成同一種最終情況,稍微變換顏色即可
* 如果最終刪除的節點是個紅節點,則不會破壞性質。相反,則基準節點路徑將少一個黑色節點。需要通過旋轉操作移過來
* 1. 如果基準節點是紅節點,則直接染黑即可。
* 2. 如果基準節點是黑節點,則看其兄弟結點。如果兄弟結點是紅色,則進行單旋移動一個節點去基準節點路徑上並保持原來的性質
* 3. 如果兄弟結點是黑色。則看兄弟結點的子結點。 分以下幾種情況
* 4. 如果子結點都不存在,或者都爲黑色,或者只存在一個且爲黑色:直接將兄弟結點染成紅色,並將基準節點上移。此時基準節點子樹路徑黑節點數量一致
* 5. 對於兄弟結點爲右子樹的情況(對稱同理)。有如下兩種情況,兩種情況需順序判斷,情況1可以轉換爲情況2
* 情況1. 如果兄弟結點的左子節點存在且爲紅色,則進行一次單旋,變換顏色可達到情況2。
* 情況2. 然後如果右結點存在且爲紅色,則進行一次單旋變換顏色即可完成。
*
*
* @author Jane
* @e-mail [email protected]
*/
public class RedBlackTtree {
// 樹根
private static Node root = null;
/**
* 開放API
*/
// 插入值
public boolean addVal(int val) {
if(root == null) {
root = new Node(val);
root.color=Color.black;
return true;
}
try {
addValue(root,val);
} catch(Exception e) {
e.printStackTrace();
return false;
}
return true;
}
// 刪除值
public boolean delVal(int val) {
try {
if(root == null)
throw new RuntimeException("tree root node is null");
this.deleteValue(root, val);
} catch (Exception e) {
return false;
}
return true;
}
// 打印序列
public void printTree() {
this.printSortedArray(root);
System.out.println();
}
// 層次打印
public void printTreeByLayer() {
if(root == null)
return;
printTreeByLayer(root);
}
public boolean validTree() {
if(isValidRedBlackTree(root)) {
System.out.println("正確的紅黑樹");
return true;
}else {
System.out.println("錯誤的紅黑樹");
return false;
}
}
/**
* 內部實現
*/
/**
* 先按照二叉搜索樹插入節點,然後從插入節點開始調整使樹維護紅黑樹的性質
* @param node 節點
* @param val 值
*/
private void addValue(Node node, int val) {
if(val<node.val && node.left!=null)
addValue(node.left, val);
else if (val<node.val && node.left==null) {
Node leaf = new Node(val);
leaf.parent=node;
node.left = leaf;
leaf.color = Color.red;
fixRedBlack(leaf);
return;
}
if(val>=node.val && node.right!=null)
addValue(node.right, val);
else if(val>=node.val && node.right == null) {
Node leaf = new Node(val);
leaf.parent=node;
node.right = leaf;
leaf.color = Color.red;
fixRedBlack(leaf);
return;
}
}
/**
* 調整節點和顏色,使得樹保持紅黑樹性質
* @param leaf 葉子節點
*/
private void fixRedBlack(Node leaf) {
// 終止條件:找到根節點,或者父節點是黑色
while(leaf.parent !=null && leaf.parent.color==Color.red) {
Node p = leaf.parent;
Node pp = p.parent;
// 父節點爲祖節點左子樹
if(p == pp.left) {
Node sr = pp.right; // 獲得叔節點
if(sr == null) { // 叔節點爲空,進行一次右旋即可
Node ppp = pp.parent;
if(leaf == p.left) {
turnRight(p);
p.color = Color.black;
pp.color = Color.red;
if(pp == ppp.left)
ppp.left = p;
else
ppp.right = p;
}
else {
turnLeftThenRight(p);
leaf.color = Color.black;
pp.color = Color.red;
if(pp == ppp.left)
ppp.left = leaf;
else
ppp.right = leaf;
}
} else if(sr.color ==Color.red) { // 叔節點爲紅色,變化顏色繼續循環
pp.color = Color.red;
sr.color = Color.black;
p.color = Color.black;
} else { // 叔節點爲黑色,做最終調整
Node ppp=pp.parent;
if(leaf == p.left) { // 直線則單旋
if(ppp == null) {
turnRight(p);
break;
}
else if(pp == ppp.left)
ppp.left = turnRight(p);
else
ppp.right = turnRight(p);
p.color=Color.black;
pp.color=Color.red;
} else { // 非直線則雙旋
if(ppp == null) {
turnLeftThenRight(p);
break;
}
if( pp ==ppp.left)
ppp.left = turnLeftThenRight(p);
else
ppp.right = turnLeftThenRight(p);
leaf.color = Color.black;
pp.color = Color.red;
}
break;
}
// 葉子節點上移
leaf = pp;
} else { // 對稱同理:父節點爲祖節點右子樹
Node sl =pp.left;
if(sl == null) {
Node ppp = pp.parent;
if(leaf == p.right) {
turnLeft(p);
pp.color = Color.red;
p.color = Color.black;
if(pp == ppp.left)
ppp.left = p;
else
ppp.right = p;
} else {
turnRightThenLeft(p);
leaf.color = Color.black;
pp.color = Color.red;
if(pp == ppp.left)
ppp.left = leaf;
else
ppp.right = leaf;
}
} else if (sl.color == Color.red) {
sl.color = Color.black;
p.color = Color.black;
pp.color = Color.red;
} else {
Node ppp = pp.parent;
if(leaf == p.left) {
if(ppp == null) {
turnRightThenLeft(p);
} else if(pp == ppp.left) {
ppp.left = turnRightThenLeft(p);
} else {
ppp.right = turnRightThenLeft(p);;
}
leaf.color = Color.black;
pp.color = Color.red;
} else {
if(ppp == null) {
turnRight(p);
} else if (pp ==ppp.left) {
ppp.left = turnRight(p);
} else {
ppp.right = turnRight(p);
}
p.color = Color.black;
pp.color = Color.red;
}
break;
}
leaf = pp;
}
}
// 保持根節點爲黑色
if(leaf.parent == null)
leaf.color=Color.black;
}
/**
* 先按照二叉搜索書刪除節點,然後從相關節點開始調整使樹保持紅黑樹性質
* @param val
*/
private void deleteValue(Node t, int val) {
if(t == null) {
return;
}
// 刪除該節點
if(t.val == val) {
Node p = t.parent;
Node leaf;
Color originColor = t.color; // 記錄被刪除節點的顏色
if(t.left == null && t.right == null) {
// 葉子節點直接刪除
transplant(t, null);
return;
}
if(t.left == null && t.right !=null) {
leaf = t.right;
transplant(t, t.right);
} else if(t.left !=null && t.right == null) {
leaf = t.left;
transplant(t, t.left);
} else {
// 找該節點右子樹一個最小結點替換要刪除的節點
Node sub = findMinNode(t.right);
originColor = sub.color;
leaf = sub.right;
sub.parent.left = leaf;
if(leaf !=null)
leaf.parent = sub.parent;
transplant(t, sub);
sub.left = t.left;
sub.color = t.color;
sub.right = t.right;
t.right.parent = sub;
sub.left.parent = sub;
t = null; // help gc
}
// 如果leaf結點爲null則往後不再有路徑,自然也無需調整
// 如果被刪除的是紅節點則性質必然繼續保持(紅結點的父節點必爲黑色,刪除紅節點不影響路徑黑節點數量)
if(leaf != null && originColor != Color.red)
fixRedBlackWhenDel(leaf);
return;
}
if(val<t.val)
deleteValue(t.left, val);
else
deleteValue(t.right, val);
}
/**
* 移植替換要刪除的節點
* @param u 待刪除的節點
* @param v 被移植的節點
*/
private void transplant(Node u, Node v) {
if(u.parent == null) {
root = v;
} else if(u == u.parent.left) {
u.parent.left=v;
} else
u.parent.right = v;
if(v != null) {
v.parent = u.parent;
}
}
private void fixRedBlackWhenDel(Node leaf) {
// 如果leaf是紅節點,只需將其顏色變黑即可保持性質
// 如果是黑節點,則包含leaf的路徑都將少一個黑節點,需要通過旋轉移一個黑節點過來,並保持其他路徑黑節點數量不變
while(leaf.color == Color.black && leaf.parent !=null) {
Node p = leaf.parent;
if(leaf == p.left) {
Node sr = p.right;
if(sr == null) {// 單路徑直接往上
leaf = p;
}else if(sr.color == Color.red) {
Node pp = p.parent;
if(pp == null) { // 當前父節點是跟節點
turnLeft(p.right);
}else if(p == pp.left) {
pp.left = turnLeft(p.right);
} else {
pp.right = turnLeft(p.right);
}
p.color = Color.red;
sr.color = Color.black;
} else {
Node wl = sr.left;
Node wr = sr.right;
if((wl==null && wr == null)||(wl==null && wr.color==Color.black)||(wr==null && wl.color == Color.black)||(wl.color==Color.black && wr.color == Color.black)) {
sr.color = Color.red;
leaf = p; // 包含p節點的路徑 黑節點數量都少1個,所以將leaf上移
} else if(wr !=null && wr.color == Color.red) {
Node pp = p.parent;
if(pp == null) {
turnLeft(sr);
}else if( p == pp.left) {
pp.left=turnLeft(sr);
}else {
pp.right=turnLeft(sr);
}
p.color = Color.black;
sr.color = Color.red;
wr.color = Color.black;
break;
} else if(wl !=null && wl.color == Color.red) {
p.right=turnRight(sr.left);
p.right.color = Color.black;
sr.color = Color.red;
}
}
} else {
Node sl = p.left;
Node pp = p.parent;
if (sl == null)
leaf = p;
else if(sl.color == Color.red) {
if(pp == null) {
turnRight(p.left);
}else if(p==pp.left) {
pp.left = turnRight(p.left);
} else
pp.right = turnLeft(p.left);
sl.color =Color.black;
p.color = Color.red;
} else {
Node wl = sl.left;
Node wr = sl.right;
if((wl==null && wr == null)||(wl==null && wr.color==Color.black)||(wr==null && wl.color == Color.black)||(wl.color==Color.black && wr.color == Color.black)) {
sl.color = Color.red;
leaf = p;
} else if(wl != null && wl.color == Color.red) {
if(pp==null) {
turnRight(p.left);
}else if(p == pp.left)
pp.left = turnRight(sl);
else
pp.right = turnRight(sl);
p.color = Color.black;
sl.color = Color.red;
wl.color = Color.black;
break;
} else if(wr != null && wr.color == Color.red) {
p.left=turnLeft(sl.right);
p.left.color= Color.black;
sl.color = Color.red;
}
}
}
}
leaf.color = Color.black;
}
private Node findMinNode(Node node) {
if(node.left == null)
return node;
return findMinNode(node.left);
}
// 右旋
private Node turnRight(Node t) {
Node p = t.parent;
p.left = t.right;
t.right = p;
p.parent =t;
return t;
}
// 左旋
private Node turnLeft(Node t) {
Node p = t.parent;
p.right = t.left;
t.left = p;
p.parent = t;
return t;
}
// 先左旋再右旋
private Node turnLeftThenRight(Node t) {
Node p = t.parent;
Node s = turnLeft(t.right);
p.left = s;
s.parent = p;
return turnRight(p.left);
}
// 先右旋轉再左旋
private Node turnRightThenLeft(Node t) {
Node p = t.parent;
Node s = turnRight(t.left);
p.right = s;
s.parent = p;
return turnLeft(p.right);
}
/**
* 輸出樹的值
*
*/
// 中序遍歷 按從小到大的順序打印
private void printSortedArray(Node node) {
if(node ==null)
return;
printSortedArray(node.left);
System.out.print(node.val + " ");
printSortedArray(node.right);
}
/**
* 按層次打印樹並輸出顏色
* @param node
*/
private void printTreeByLayer(Node node) {
int count = 0;
int layerLen = 0;
int nextLayerLen = 0;
LinkedBlockingQueue<Node> queue = new LinkedBlockingQueue<>();
queue.offer(node);
layerLen++;
while(!queue.isEmpty()) {
Node t = queue.remove();
count++;
if(t.left != null) {
queue.offer(t.left);
nextLayerLen++;
}
if(t.right != null) {
queue.offer(t.right);
nextLayerLen++;
}
System.out.print(t.val + "-" + t.color.name());
if(layerLen == count) {
System.out.println();
layerLen = nextLayerLen;
nextLayerLen = 0;
count=0;
} else {
System.out.print(" ");
}
}
}
/**
* 校驗紅黑樹是否正確
*/
private boolean isValidRedBlackTree(Node root) {
if(root == null)
return true;
// 根節點校驗
if(root.color != Color.black) {
return false;
}
// 連續紅節點校驗
if(!validColor(root, false))
return false;
// 路徑黑節點數量校驗
if(!validBlackCount(root))
return false;
// 值順序校驗
if(!validSortValue(root))
return false;
// 滿足所有必要條件,則充分證明是棵發育良好的紅黑樹
return true;
}
private boolean validBlackCount(Node root) {
if(countBlackNodes(root)<0)
return false;
return true;
}
private int countBlackNodes(Node root) {
int cur = 0;
if(root.color == Color.black)
cur++;
int countLeft=0;
int countRight=0;
if(root.left == null && root.right == null)
return cur;
if(root.left != null)
countLeft = countBlackNodes(root.left);
if(root.right != null)
countRight = countBlackNodes(root.right);
if(root.left == null && root.right !=null)
return countRight + cur;
if(root.left != null && root.right == null)
return countLeft + cur;
if(countLeft == -1 || countRight == -1 || countLeft != countRight)
return -1;
return countLeft + cur;
}
private boolean validSortValue(Node root) {
boolean left = true;
boolean right = true;
if(root.left!=null&& root.left.val<root.val) {
left = validSortValue(root.left);
} else if( root.left!=null && root.left.val >= root.val)
return false;
if(root.right!=null && root.right.val >= root.val)
right = validSortValue(root.right);
else if(root.right!=null && root.right.val < root.val)
return false;
return left && right;
}
private boolean validColor(Node root, boolean parentRed) {
if(root.color == Color.red && parentRed) // 路徑連續紅節點
return false;
else if(root.color == Color.red)
parentRed = true;
else if(root.color == Color.black)
parentRed = false;
boolean left=true;
boolean right=true;
if(root.left!=null)
left=validColor(root.left, parentRed);
if(root.right!=null)
right=validColor(root.right, parentRed);
return left && right;
}
/**
* 節點設計
* 包含一個帶有父節點的引用
* @author 75279
*
*/
class Node{
Node left;
Node right;
Color color;
Node parent;
int val;
Node(int val){
this.val=val;
}
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 Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
public Node getParent() {
return parent;
}
public void setParent(Node parent) {
this.parent = parent;
}
public int getVal() {
return val;
}
public void setVal(int val) {
this.val = val;
}
}
// 顏色枚舉
enum Color{
red(1),
black(0);
Color(int val){
value =val;
}
int value;
public int getColor(){
return value;
}
}
}
二、測試主類:
import tree.RedBlackTtree;
public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
RedBlackTtree redBlackTtree = new RedBlackTtree();
redBlackTtree.addVal(13);
redBlackTtree.addVal(51);
redBlackTtree.addVal(1);
redBlackTtree.validTree();
redBlackTtree.addVal(33);
redBlackTtree.addVal(150);
redBlackTtree.addVal(6);
redBlackTtree.validTree();
redBlackTtree.addVal(12);
redBlackTtree.addVal(15);
redBlackTtree.addVal(11);
redBlackTtree.validTree();
redBlackTtree.addVal(25);
redBlackTtree.validTree();
redBlackTtree.printTree();
redBlackTtree.printTreeByLayer();
// 刪除一個節點 並校驗 並打印
redBlackTtree.delVal(150);
redBlackTtree.validTree();
redBlackTtree.delVal(12);
redBlackTtree.validTree();
redBlackTtree.delVal(33);
redBlackTtree.validTree();
redBlackTtree.delVal(1);
redBlackTtree.validTree();
redBlackTtree.delVal(51);
redBlackTtree.validTree();
redBlackTtree.printTree();
redBlackTtree.printTreeByLayer();
}
}