1. 樹的定義
樹是由N個結點構成的有限集合,每個節點都有0個或者多個互不相交的樹,稱爲結點的子樹。
- 結點的度:結點的子樹個數
- 樹的度:樹中所有結點中最大的度
- 葉結點:度爲0的結點
- 父結點:有子樹的結點是其子樹的根結點的父結點
- 子樹是不相交的
2. 二叉樹
度爲2的樹,子樹有左右順序之分。
3. 哈夫曼樹
一棵二叉樹,該樹的帶權路徑長度達到最小,稱爲最優二叉樹,也稱爲哈夫曼樹(Huffman Tree)。
3.1 構造方式
每次把權值最小的兩棵二叉樹合併,左節點權值比右節點小。
3.2 適用場景
給定一段字符串,利用哈夫曼樹對字符串進行編碼,使得該字符串的編碼存儲空間最少。
3.3 代碼樣例
public class HuffmanTree {
public static class Node<E> {
E data; //數據
int weight; //權重
Node leftChild; //左子節點
Node rightChild;//右子節點
public Node(E data, int weight) {
super();
this.data = data;
this.weight = weight;
}
public String toString() {
return "Node[" + weight + ",data=" + data + "]";
}
}
public static void main(String[] args) {
//創建樣例數據
List<Node> nodes = createSampleData();
//創建哈弗曼樹
Node root = createTree(nodes);
printTree(root);
}
public static void printTree(Node root){
System.out.println(root.toString());
if (root.leftChild !=null) {
System.out.print("left:");
printTree(root.leftChild);
}
if (root.rightChild !=null) {
System.out.print("right:");
printTree(root.rightChild);
}
}
public static Node createTree(List<Node> nodes){
while(nodes.size()>1){
sort(nodes); //根據權重排序
Node left = nodes.get(0);//權重最小的
Node right = nodes.get(1);//權重第二小的
//生成一個新的節點(父節點),父節點的權重爲兩個子節點的之和
Node parent = new Node(null,left.weight+right.weight);
//樹的連接,讓子節點與父節點進行連接
parent.leftChild = left;
parent.rightChild = right;
nodes.remove(0);//刪除最小的
nodes.remove(0);//刪除第二小的。
nodes.add(parent);
}
return nodes.get(0); //返回根節點
}
public static List<Node> createSampleData(){
List<Node> nodes = new ArrayList<Node>();
nodes.add(new Node("a", 10));
nodes.add(new Node("b", 15));
nodes.add(new Node("c", 12));
nodes.add(new Node("d", 3));
nodes.add(new Node("e", 4));
nodes.add(new Node("f", 13));
nodes.add(new Node("g", 1));
return nodes;
}
public static void sort(List<Node> nodes) {
if (nodes.size() <= 1)
return ;
for (int i = 0; i < nodes.size(); i++){
for (int j = 0; j < nodes.size() - 1 - i; j++){
if (nodes.get(j + 1).weight < nodes.get(j).weight) {
Node temp = nodes.get(j + 1);
nodes.set(j+1,nodes.get(j));
nodes.set(j,temp);
}
}
}
return ;
}
}
4. 二叉排序樹
- 左子樹的所有的值小於根節點的值
- 右子樹的所有的值大於根節點的值
- 左、右子樹滿足以上兩點
4.1 二叉排序樹查詢
查找的值X從根節點開始
- 如果X小於根節點值,則在左子樹中繼續查找;
- 如果X大於根節點值,則在右子樹中繼續查找;
- 如果X值等於根節點值,則返回該節點;
- 如果都查不到,則返回空Null;
4.2 二叉排序樹插入
插入的值X從根節點開始查找
- X值小於該節點值,在左子樹中繼續;
- X值大於該節點值,在右子樹中繼續;
- 如果節點是葉節點,X值小於該節點值則插入左子節點,否則插入右節點
刪除操作類似,不介紹了。
5. 平衡二叉樹
平衡二叉樹(AVL樹,Balance Binary Search Tree )是一 棵二叉排序樹,它的左右兩個子樹的高度差(平衡因子)的絕對值不超過1,並且左右兩個子樹都是一棵平衡二叉樹。這樣樹的高度最低,因爲樹查找的效率決定於樹的高度。
一顆平衡二叉樹,如果新插入一個元素,會有兩個結果:平衡沒有被打破,不用調整;平衡被打破,需要調整。
5.1 LL旋轉
插入節點在失衡結點的左子樹的左邊,只需要經過一次左旋即可達到平衡。
/*
* LL旋轉
* 左旋示意圖(對結點20進行左旋)
* 30 20
* / \ / \
* 20 40 10 30
* / \ --LL旋轉- / / \
* 10 25 5 25 40
* /
* 5
*
*/
5.2 RR旋轉
插入節點在失衡結點的右子樹的右邊,只需要經過一次右旋即可達到平衡。
/*
* RR旋轉
* 右旋示意圖(對結點30進行左旋)
* 20 30
* / \ / \
* 10 30 20 40
* / \ --RR旋轉- / \ \
* 25 40 10 25 50
* \
* 50
*
*/
5.3 LR旋轉
插入節點在失衡結點的左子樹的右邊,失衡結點的左子樹先做RR旋轉,失衡結點再做LL旋轉也可達到平衡。
5.4 RL旋轉
插入節點在失衡結點的右子樹的左邊,失衡結點的右子樹先做LL旋轉,失衡結點再做RR旋轉也可達到平衡。
5.5 代碼演示
public class AVLTree {
//節點
public static class Node {
int data; //數據
Node leftChild; //左子節點
Node rightChild;//右子節點
int height; // 記錄該節點所在的高度
public Node(int data) {
this.data = data;
}
}
//獲取節點的高度
public static int getHeight(Node p){
return p == null ? -1 : p.height; // 空樹的高度爲-1
}
public static void main(String[] args) {
Node root = null;
root = insert(root,30);
root = insert(root,20);
root = insert(root,40);
root = insert(root,10);
root = insert(root,25);
//插入節點在失衡結點的左子樹的左邊
root = insert(root,5);
//打印樹,按照先打印左子樹,再打印右子樹的方式
printTree(root);
}
public static void printTree(Node root) {
System.out.println(root.data);
if(root.leftChild !=null){
System.out.print("left:");
printTree(root.leftChild);
}
if(root.rightChild !=null){
System.out.print("right:");
printTree(root.rightChild);
}
}
// AVL樹的插入方法
public static Node insert(Node root, int data) {
if (root == null) {
root = new Node(data);
return root;
}
if (data <= root.data) { // 插入到其左子樹上
root.leftChild = insert(root.leftChild, data);
//平衡調整
if (getHeight(root.leftChild) - getHeight(root.rightChild) > 1) {
if (data <= root.leftChild.data) { // 插入節點在失衡結點的左子樹的左邊
System.out.println("LL旋轉");
root = LLRotate(root); // LL旋轉調整
}else{ // 插入節點在失衡結點的左子樹的右邊
System.out.println("LR旋轉");
root = LRRotate(root);
}
}
}else{ // 插入到其右子樹上
root.rightChild = insert(root.rightChild, data);
//平衡調整
if(getHeight(root.rightChild) - getHeight(root.leftChild) > 1){
if(data <= root.rightChild.data){//插入節點在失衡結點的右子樹的左邊
System.out.println("RL旋轉");
root = RLRotate(root);
}else{
System.out.println("RR旋轉");//插入節點在失衡結點的右子樹的右邊
root = RRRotate(root);
}
}
}
//重新調整root節點的高度值
root.height = Math.max(getHeight(root.leftChild), getHeight(root.rightChild)) + 1;
return root;
}
// LR旋轉
public static Node LRRotate(Node p){
p.leftChild = RRRotate(p.leftChild); // 先將失衡點p的左子樹進行RR旋轉
return LLRotate(p); // 再將失衡點p進行LL平衡旋轉並返回新節點代替原失衡點p
}
// RL平衡旋轉
public static Node RLRotate(Node p){
p.rightChild = LLRotate(p.rightChild); // 先將失衡點p的右子樹進行LL平衡旋轉
return RRRotate(p); // 再將失衡點p進行RR平衡旋轉並返回新節點代替原失衡點p
}
/*
* LL旋轉
* 左旋示意圖(對結點20進行左旋)
* 30 20
* / \ / \
* 20 40 10 30
* / \ --LL旋轉- / / \
* 10 25 5 25 40
* /
* 5
*
*/
public static Node LLRotate(Node p){ // 30爲失衡點
Node lsubtree = p.leftChild; //失衡點的左子樹的根結點20作爲新的結點
p.leftChild = lsubtree.rightChild; //將新節點的右子樹25成爲失衡點30的左子樹
lsubtree.rightChild = p; // 將失衡點30作爲新結點的右子樹
// 重新設置失衡點30和新節點20的高度
p.height = Math.max(getHeight(p.leftChild), getHeight(p.rightChild)) + 1;
lsubtree.height = Math.max(getHeight(lsubtree.leftChild), p.height) + 1;
return lsubtree; // 新的根節點取代原失衡點的位置
}
/*
* RR旋轉
* 右旋示意圖(對結點30進行左旋)
* 20 30
* / \ / \
* 10 30 20 40
* / \ --RR旋轉- / \ \
* 25 40 10 25 50
* \
* 50
*
*/
// RR旋轉
public static Node RRRotate(Node p){ // 20爲失衡點
Node rsubtree = p.rightChild; //失衡點的右子樹的根結點30作爲新的結點
p.rightChild = rsubtree.leftChild; //將新節點的左子樹25成爲失衡點20的右子樹
rsubtree.leftChild = p; // 將失衡點20作爲新結點的左子樹
// 重新設置失衡點20和新節點30的高度
p.height = Math.max(getHeight(p.leftChild), getHeight(p.rightChild)) + 1;
rsubtree.height = Math.max(getHeight(rsubtree.leftChild), getHeight(rsubtree.rightChild)) + 1;
return rsubtree; // 新的根節點取代原失衡點的位置
}
}
6. 紅黑樹
紅黑樹(R-B Tree,Red-Black Tree)它一種特殊的二叉查找樹,同時具備以下特徵:
- 節點非紅即黑
- 根節點是黑色
- 所有NUll節點稱爲葉子節點,且認爲顏色爲黑
- 所有紅色節點的子節點都爲黑色
- 從任一節點到其葉子節點的所有路徑上都包含相同數目的黑節點
- 從根到葉子的最長的路徑不多於最短的可能路徑的兩倍長
- 從任一節點到其葉子節點的所有路徑上都包含相同數目的黑節點
6.1 紅黑樹插入
插入原則:因爲插入節點的顏色如果爲黑肯定破壞紅黑樹性質5,所以每次插入的點首先都是紅結點。
- 情況1:插入的新節點N位於樹的根上、插入的新節點的父節點是黑色
- 情況2:如果新節點的父節點和叔父節點都是紅色節點,先插入新節點(紅色),(變色)新節點的父節點、叔父節點、祖父節點都需要變色。
- 情況3:如果新節點的父節點是紅色同時叔父節點都是黑色,同時新節點是其父節點的左子節點而父節點又是其父節點的左子節點。我們進行一次右旋轉調換新節點和其父節點的角色(以父節點爲軸)
- 情況4:如果新節點的父節點是紅色同時叔父節點都是黑色,同時新節點是其父節點的右子節點而父節點又是其父節點的右子節點。我們對祖父節點進行一次左旋轉調換新節點和其父節點的角色(以父節點爲軸)
- 情況5:如果新節點的父節點是紅色同時叔父節點都是黑色,同時新節點是其父節點的右子節點而父節點又是其父節點的左子節點。我們進行一次左旋轉調換新節點和其父節點的角色,同時再進行一次右旋轉
- 情況6:如果新節點的父節點是紅色同時叔父節點都是黑色,同時新節點是其父節點的左子節點而父節點又是其父節點的右子節點。我們進行一次右旋轉調換新節點和其父節點的角色,再進行一次左旋轉(第二次旋轉)
先貼一段別人代碼,有時間再研究下。
public class RBTree<T extends Comparable<T>> {
private RBTNode<T> mRoot; // 根結點
private static final boolean RED = false;//紅色是false
private static final boolean BLACK = true;//黑色是true
public class RBTNode<T extends Comparable<T>> {
boolean color; // 顏色
T key; // 關鍵字(鍵值)
RBTNode<T> left; // 左孩子
RBTNode<T> right; // 右孩子
RBTNode<T> parent; // 父結點
public RBTNode(T key, boolean color, RBTNode<T> parent, RBTNode<T> left, RBTNode<T> right) {
this.key = key;
this.color = color;
this.parent = parent;
this.left = left;
this.right = right;
}
public T getKey() {
return key;
}
public String toString() {
return ""+key+(this.color==RED?"(R)":"B");
}
}
public RBTree() {
mRoot=null;
}
private RBTNode<T> parentOf(RBTNode<T> node) {
return node!=null ? node.parent : null;
}
private boolean colorOf(RBTNode<T> node) {
return node!=null ? node.color : BLACK;
}
private boolean isRed(RBTNode<T> node) {
return ((node!=null)&&(node.color==RED)) ? true : false;
}
private boolean isBlack(RBTNode<T> node) {
return !isRed(node);
}
private void setBlack(RBTNode<T> node) {
if (node!=null)
node.color = BLACK;
}
private void setRed(RBTNode<T> node) {
if (node!=null)
node.color = RED;
}
private void setParent(RBTNode<T> node, RBTNode<T> parent) {
if (node!=null)
node.parent = parent;
}
private void setColor(RBTNode<T> node, boolean color) {
if (node!=null)
node.color = color;
}
/*
* (遞歸實現)查找"紅黑樹x"中鍵值爲key的節點
*/
private RBTNode<T> search(RBTNode<T> x, T key) {
if (x==null)
return x;
int cmp = key.compareTo(x.key);
if (cmp < 0)
return search(x.left, key);
else if (cmp > 0)
return search(x.right, key);
else
return x;
}
public RBTNode<T> search(T key) {
return search(mRoot, key);
}
/*
* (非遞歸實現)查找"紅黑樹x"中鍵值爲key的節點
*/
private RBTNode<T> iterativeSearch(RBTNode<T> x, T key) {
while (x!=null) {
int cmp = key.compareTo(x.key);
if (cmp < 0)
x = x.left;
else if (cmp > 0)
x = x.right;
else
return x;
}
return x;
}
public RBTNode<T> iterativeSearch(T key) {
return iterativeSearch(mRoot, key);
}
/*
* 查找最小結點:返回tree爲根結點的紅黑樹的最小結點。
*/
private RBTNode<T> minimum(RBTNode<T> tree) {
if (tree == null)
return null;
while(tree.left != null)
tree = tree.left;
return tree;
}
public T minimum() {
RBTNode<T> p = minimum(mRoot);
if (p != null)
return p.key;
return null;
}
/*
* 查找最大結點:返回tree爲根結點的紅黑樹的最大結點。
*/
private RBTNode<T> maximum(RBTNode<T> tree) {
if (tree == null)
return null;
while(tree.right != null)
tree = tree.right;
return tree;
}
public T maximum() {
RBTNode<T> p = maximum(mRoot);
if (p != null)
return p.key;
return null;
}
/*
* 找結點(x)的後繼結點。即,查找"紅黑樹中數據值大於該結點"的"最小結點"。
*/
public RBTNode<T> successor(RBTNode<T> x) {
// 如果x存在右孩子,則"x的後繼結點"爲 "以其右孩子爲根的子樹的最小結點"。
if (x.right != null)
return minimum(x.right);
// 如果x沒有右孩子。則x有以下兩種可能:
// (01) x是"一個左孩子",則"x的後繼結點"爲 "它的父結點"。
// (02) x是"一個右孩子",則查找"x的最低的父結點,並且該父結點要具有左孩子",找到的這個"最低的父結點"就是"x的後繼結點"。
RBTNode<T> y = x.parent;
while ((y!=null) && (x==y.right)) {
x = y;
y = y.parent;
}
return y;
}
/*
* 找結點(x)的前驅結點。即,查找"紅黑樹中數據值小於該結點"的"最大結點"。
*/
public RBTNode<T> predecessor(RBTNode<T> x) {
// 如果x存在左孩子,則"x的前驅結點"爲 "以其左孩子爲根的子樹的最大結點"。
if (x.left != null)
return maximum(x.left);
// 如果x沒有左孩子。則x有以下兩種可能:
// (01) x是"一個右孩子",則"x的前驅結點"爲 "它的父結點"。
// (01) x是"一個左孩子",則查找"x的最低的父結點,並且該父結點要具有右孩子",找到的這個"最低的父結點"就是"x的前驅結點"。
RBTNode<T> y = x.parent;
while ((y!=null) && (x==y.left)) {
x = y;
y = y.parent;
}
return y;
}
/*
* 對紅黑樹的節點(x)進行左旋轉
*
* 左旋示意圖(對節點x進行左旋):
* 13 17
* / \ / \
* nul 17 13 27
* / \ --(左旋)-. / \ / \
* nul 27 nul nul nul nul
* / \
* nul nul
*
*
*/
private void leftRotate(RBTNode<T> x) {
// 設置x的右孩子爲y
RBTNode<T> y = x.right;
// 將 “y的左孩子” 設爲 “x的右孩子”;
// 如果y的左孩子非空,將 “x” 設爲 “y的左孩子的父親”
x.right = y.left;
if (y.left != null)
y.left.parent = x;
// 將 “x的父親” 設爲 “y的父親”
y.parent = x.parent;
if (x.parent == null) {
this.mRoot = y; // 如果 “x的父親” 是空節點,則將y設爲根節點
} else {
if (x.parent.left == x)
x.parent.left = y; // 如果 x是它父節點的左孩子,則將y設爲“x的父節點的左孩子”
else
x.parent.right = y; // 如果 x是它父節點的左孩子,則將y設爲“x的父節點的左孩子”
}
// 將 “x” 設爲 “y的左孩子”
y.left = x;
// 將 “x的父節點” 設爲 “y”
x.parent = y;
}
/*
* 對紅黑樹的節點(8)進行右旋轉
*
* 右旋示意圖(對節點8進行右旋):
* 13 8
* / \ / \
* 8 nul 1 13
* / \ --(右旋)- / \ / \
* 1 nul nul nul nul nul
* / \
* nul nul
*
*/
private void rightRotate(RBTNode<T> y) {
// 設置x是當前節點的左孩子。
RBTNode<T> x = y.left;
// 將 “x的右孩子” 設爲 “y的左孩子”;
// 如果"x的右孩子"不爲空的話,將 “y” 設爲 “x的右孩子的父親”
y.left = x.right;
if (x.right != null)
x.right.parent = y;
// 將 “y的父親” 設爲 “x的父親”
x.parent = y.parent;
if (y.parent == null) {
this.mRoot = x; // 如果 “y的父親” 是空節點,則將x設爲根節點
} else {
if (y == y.parent.right)
y.parent.right = x; // 如果 y是它父節點的右孩子,則將x設爲“y的父節點的右孩子”
else
y.parent.left = x; // (y是它父節點的左孩子) 將x設爲“x的父節點的左孩子”
}
// 將 “y” 設爲 “x的右孩子”
x.right = y;
// 將 “y的父節點” 設爲 “x”
y.parent = x;
}
/*
* 紅黑樹插入修正函數
*
* 在向紅黑樹中插入節點之後(失去平衡),再調用該函數;
* 目的是將它重新塑造成一顆紅黑樹。
*
* 參數說明:
* node 插入的結點 // 對應《算法導論》中的z
*/
private void insertFixUp(RBTNode<T> node) {
RBTNode<T> parent, gparent;
// 若“父節點存在,並且父節點的顏色是紅色”
while (((parent = parentOf(node))!=null) && isRed(parent)) {
gparent = parentOf(parent);//祖父節點
//若“父節點”是“祖父節點的左孩子”
if (parent == gparent.left) {
RBTNode<T> uncle = gparent.right;
if ((uncle!=null) && isRed(uncle)) { // 情況2:叔叔節點是紅色
setBlack(uncle);
setBlack(parent);
setRed(gparent);
node = gparent;
continue;
}
// 情況5:叔叔是黑色,且當前節點是右孩子(兩次旋轉,先左後右)
if (parent.right == node) {
RBTNode<T> tmp;
leftRotate(parent); //左旋轉
tmp = parent;
parent = node;
node = tmp;
}
// 情況3:叔叔是黑色,且當前節點是左孩子。(一次右旋轉)
setBlack(parent);
setRed(gparent);
rightRotate(gparent);
} else { //若“父節點”是“祖父節點的右孩子”
RBTNode<T> uncle = gparent.left;
if ((uncle!=null) && isRed(uncle)) { // 情況2:叔叔節點是紅色
setBlack(uncle);
setBlack(parent);
setRed(gparent);
node = gparent;
continue;
}
// 情況6:叔叔是黑色,且當前節點是左孩子(兩次旋轉,先右後左)
if (parent.left == node) {
RBTNode<T> tmp;
rightRotate(parent);
tmp = parent;
parent = node;
node = tmp;
}
// 情況4:叔叔是黑色,且當前節點是右孩子。(一次左旋轉)
setBlack(parent);
setRed(gparent);
leftRotate(gparent);
}
}
// 將根節點設爲黑色
setBlack(this.mRoot);
}
/*
* 將結點插入到紅黑樹中
*
* 參數說明:
* node 插入的結點
*/
private void insert(RBTNode<T> node) {
int cmp;
RBTNode<T> y = null;
RBTNode<T> x = this.mRoot;
// 1. 將紅黑樹當作一顆二叉查找樹,將節點添加到二叉查找樹中。
while (x != null) {
y = x;
cmp = node.key.compareTo(x.key);
if (cmp < 0)
x = x.left;
else
x = x.right;
}
node.parent = y;
if (y!=null) {
cmp = node.key.compareTo(y.key);
if (cmp < 0)
y.left = node;
else
y.right = node;
} else {
this.mRoot = node;
}
// 2. 設置節點的顏色爲紅色
node.color = RED;
// 3. 將它重新修正爲一顆二叉查找樹
insertFixUp(node);
}
/*
* 新建結點(key),並將其插入到紅黑樹中
*
* 參數說明:
* key 插入結點的鍵值
*/
public void insert(T key) {
RBTNode<T> node=new RBTNode<T>(key,BLACK,null,null,null);
// 如果新建結點失敗,則返回。
if (node != null)
insert(node);
}
/*
* 紅黑樹刪除修正函數
*
* 在從紅黑樹中刪除插入節點之後(紅黑樹失去平衡),再調用該函數;
* 目的是將它重新塑造成一顆紅黑樹。
*
* 參數說明:
* node 待修正的節點
*/
private void removeFixUp(RBTNode<T> node, RBTNode<T> parent) {
RBTNode<T> other;
while ((node==null || isBlack(node)) && (node != this.mRoot)) {
if (parent.left == node) {
other = parent.right;
if (isRed(other)) {
// Case 1: x的兄弟w是紅色的
setBlack(other);
setRed(parent);
leftRotate(parent);
other = parent.right;
}
if ((other.left==null || isBlack(other.left)) &&
(other.right==null || isBlack(other.right))) {
// Case 2: x的兄弟w是黑色,且w的倆個孩子也都是黑色的
setRed(other);
node = parent;
parent = parentOf(node);
} else {
if (other.right==null || isBlack(other.right)) {
// Case 3: x的兄弟w是黑色的,並且w的左孩子是紅色,右孩子爲黑色。
setBlack(other.left);
setRed(other);
rightRotate(other);
other = parent.right;
}
// Case 4: x的兄弟w是黑色的;並且w的右孩子是紅色的,左孩子任意顏色。
setColor(other, colorOf(parent));
setBlack(parent);
setBlack(other.right);
leftRotate(parent);
node = this.mRoot;
break;
}
} else {
other = parent.left;
if (isRed(other)) {
// Case 1: x的兄弟w是紅色的
setBlack(other);
setRed(parent);
rightRotate(parent);
other = parent.left;
}
if ((other.left==null || isBlack(other.left)) &&
(other.right==null || isBlack(other.right))) {
// Case 2: x的兄弟w是黑色,且w的倆個孩子也都是黑色的
setRed(other);
node = parent;
parent = parentOf(node);
} else {
if (other.left==null || isBlack(other.left)) {
// Case 3: x的兄弟w是黑色的,並且w的左孩子是紅色,右孩子爲黑色。
setBlack(other.right);
setRed(other);
leftRotate(other);
other = parent.left;
}
// Case 4: x的兄弟w是黑色的;並且w的右孩子是紅色的,左孩子任意顏色。
setColor(other, colorOf(parent));
setBlack(parent);
setBlack(other.left);
rightRotate(parent);
node = this.mRoot;
break;
}
}
}
if (node!=null)
setBlack(node);
}
/*
* 刪除結點(node),並返回被刪除的結點
*
* 參數說明:
* node 刪除的結點
*/
private void remove(RBTNode<T> node) {
RBTNode<T> child, parent;
boolean color;
// 被刪除節點的"左右孩子都不爲空"的情況。
if ( (node.left!=null) && (node.right!=null) ) {
// 被刪節點的後繼節點。(稱爲"取代節點")
// 用它來取代"被刪節點"的位置,然後再將"被刪節點"去掉。
RBTNode<T> replace = node;
// 獲取後繼節點
replace = replace.right;
while (replace.left != null)
replace = replace.left;
// "node節點"不是根節點(只有根節點不存在父節點)
if (parentOf(node)!=null) {
if (parentOf(node).left == node)
parentOf(node).left = replace;
else
parentOf(node).right = replace;
} else {
// "node節點"是根節點,更新根節點。
this.mRoot = replace;
}
// child是"取代節點"的右孩子,也是需要"調整的節點"。
// "取代節點"肯定不存在左孩子!因爲它是一個後繼節點。
child = replace.right;
parent = parentOf(replace);
// 保存"取代節點"的顏色
color = colorOf(replace);
// "被刪除節點"是"它的後繼節點的父節點"
if (parent == node) {
parent = replace;
} else {
// child不爲空
if (child!=null)
setParent(child, parent);
parent.left = child;
replace.right = node.right;
setParent(node.right, replace);
}
replace.parent = node.parent;
replace.color = node.color;
replace.left = node.left;
node.left.parent = replace;
if (color == BLACK)
removeFixUp(child, parent);
node = null;
return ;
}
if (node.left !=null) {
child = node.left;
} else {
child = node.right;
}
parent = node.parent;
// 保存"取代節點"的顏色
color = node.color;
if (child!=null)
child.parent = parent;
// "node節點"不是根節點
if (parent!=null) {
if (parent.left == node)
parent.left = child;
else
parent.right = child;
} else {
this.mRoot = child;
}
if (color == BLACK)
removeFixUp(child, parent);
node = null;
}
/*
* 刪除結點(z),並返回被刪除的結點
*
* 參數說明:
* tree 紅黑樹的根結點
* z 刪除的結點
*/
public void remove(T key) {
RBTNode<T> node;
if ((node = search(mRoot, key)) != null)
remove(node);
}
/*
* 銷燬紅黑樹
*/
private void destroy(RBTNode<T> tree) {
if (tree==null)
return ;
if (tree.left != null)
destroy(tree.left);
if (tree.right != null)
destroy(tree.right);
tree=null;
}
public void clear() {
destroy(mRoot);
mRoot = null;
}
/*
* 打印"紅黑樹"
*
* key -- 節點的鍵值
* direction -- 0,表示該節點是根節點;
* -1,表示該節點是它的父結點的左孩子;
* 1,表示該節點是它的父結點的右孩子。
*/
private void print(RBTNode<T> tree, T key, int direction) {
if(tree != null) {
if(direction==0) // tree是根節點
System.out.printf("%2d(B) is root\n", tree.key);
else // tree是分支節點
System.out.printf("%2d(%s) is %2d's %6s child\n", tree.key, isRed(tree)?"R":"B", key, direction==1?"right" : "left");
print(tree.left, tree.key, -1);
print(tree.right,tree.key, 1);
}
}
public void print() {
if (mRoot != null)
print(mRoot, mRoot.key, 0);
}
/*
* 前序遍歷"紅黑樹"
*/
private void preOrder(RBTNode<T> tree) {
if(tree != null) {
System.out.print(tree.key+" ");
preOrder(tree.left);
preOrder(tree.right);
}
}
public void preOrder() {
preOrder(mRoot);
}
/*
* 中序遍歷"紅黑樹"
*/
private void inOrder(RBTNode<T> tree) {
if(tree != null) {
inOrder(tree.left);
System.out.print(tree.key+" ");
inOrder(tree.right);
}
}
public void inOrder() {
inOrder(mRoot);
}
/*
* 後序遍歷"紅黑樹"
*/
private void postOrder(RBTNode<T> tree) {
if(tree != null)
{
postOrder(tree.left);
postOrder(tree.right);
System.out.print(tree.key+" ");
}
}
public void postOrder() {
postOrder(mRoot);
}
}