1、平衡二叉樹定義:
平衡二叉樹(Balanced Binary Tree或Height-Balanced Tree)又稱AVL樹。它或者是一顆空樹,或者是具有下列性質的二叉樹:它的左子樹和右子樹都是平衡二叉樹,且左子樹和右子樹的深度之差的絕對值不超過1。若將二叉樹上結點的平衡因子bf(balance factor)定義為該結點的左子樹的深度減去右子樹的深度,則平衡二叉樹上所有結點的平衡因子只可能為-1、0和1這三個值。
2、失去平衡情況分析:
假設結點A是一顆子平衡二叉樹,當在以A為根結點的AVL樹上插入一個新結點時,會出現以下三種情況:
1)如果插入前A—>bf=1(A的左子樹深度比右子樹深度多1),如果插入在A的左子樹上且A的左子樹深度增加了1,則此時A—>bf=2需要對樹進行調整,如圖2.1結點C為新插入的結點,C可以插入到B的左子樹上(如圖2.1(b))或者右子樹上(如圖2.1(c))。
2)如果插入前A—>bf=0(A的左子樹和右子樹深度相等),如果插入在A的左子樹上且A的左子樹深度增加了1,則此時只需要改變A的平衡因子為1即可不需要對樹進行調整。如果插入在A的右子樹上且A的右子樹深度增加了1,則此時只需要改變A的平衡因子為-1即可不需要進行調整。
3)如果插入前A—>bf=-1(A的左子樹深度比右子樹深度少1),如果插入在A的右子樹上且A的右子樹深度增加了1,則此時A—>bf=-2需要對樹進行調整,如圖2.2結點C為新插入的結點,C可以插入在B的左子樹上(如圖2.2(b))或者右子樹上(如圖2.2(c))。
圖2.1 圖2.2
注意:上圖中為了清楚的看到添加結點后失去平衡時的情況,省去了一些子結點,這些結點在下面的分析中會完整畫出來
當出現圖2.1(b)中的情況時只需要進行一次右旋轉操作,旋轉后得到如圖2.1(d)所示的平衡二叉樹。
當出現圖2.1(c)中的情況時需要先對A的左子樹B進行左旋操作,然后再進行右旋操作,旋轉后得到如圖2.1(e)所示的平衡二叉樹。
當出現圖2.2(b)中的情況時只需要進行一次右旋轉操作,旋轉后得到如圖2.1(d)所示的平衡二叉樹。
當出現圖2.2(c)中的情況時需要先對A的右子樹B進行右旋,然后再進行左旋操作,旋轉后得到如圖2.2(e)所示的平衡二叉樹。
3.求旋轉后各結點的平衡因子:
旋轉后怎么確定各結點的新的平衡因子是平衡二叉樹算法的關鍵點,我們需要按情況來一一推理。
一、當出現圖2.1(b)(c)這兩種情況時,需進行左平衡處理:
1)當新結點插入到B的左子樹上時B—>bf=1,由此可知:deep(C)=deep(E)+1,deep(B)=deep(C)+1;由於插入新結點前A—>bf=1,deep(B)=deep(D)+1則插入新節點后deep(B)=deep(D)+2;圖3.1.1為調整前的二叉樹,圖3.1.2為對A樹進行右旋轉后的AVL樹:
圖3.1.1 圖3.1.2
對比圖3.1.1和3.1.2可知旋轉后的新樹中A的左子樹發生了變化,B的右子樹發生了變化,其他結點都沒變;因此只需要重新算出A的平衡因子和B的平衡因子即可證明調整后的樹是否為AVL樹。
由上面的等式deep(B)=deep(D)+2,deep(B)=deep(C)+1,deep(C)=deep(E)+1
可以推出deep(E)=deep(C)-1=deep(B)-1-1=deep(D)+2-1-1=deep(D)可得出A—>bf=0
由調整后deep(E)=deep(D)可推出調整后deep(A)=deep(E)+1=deep(C)-1+1=deep(C)可得出B—>bf=0;
2)當新結點插入到B的右子樹上時B—>bf=-1,由此可知:deep(C)=deep(E)-1,deep(B)=deep(E)+1;由於插入新結點前A—>bf=1,deep(B)=deep(D)+1則插入新節點后deep(B)=deep(D)+2;圖3.2.1為調整前的二叉樹,圖3.2.2為先對B樹進行左旋然后對A樹進行右旋后的AVL樹:
圖3.2.1 圖3.2.2
對比圖3.2.1和3.2.2可知調整后的新樹中A的左子樹發生了變化,B的右子樹發生了變化,E的左右子樹都發生了變化,其他結點都沒變,因此只需要重新算出A的平衡因子、B的平衡因子以及E的平衡因子即可證明調整后的樹是否為AVL樹。
此時由於調整后的B和A結點的平衡因子與E的左右子樹EL和ER有關,因此需要根據E的平衡因子的不同來進行分析:
由上面的分析可得到deep(B)=deep(D)+2,deep(B)=deep(E)+1,deep(C)=deep(E)-1
1、當E—>bf=1時:deep(E)=deep(EL)+1,deep(ER)=deep(EL)-1
deep(C)=deep(E)-1=deep(EL)+1-1=deep(EL)可得B—>bf=0
deep(D)=deep(B)-2=deep(E)+1-2=deep(ER)+1+1+1-2=deep(ER)+1可得A—>bf=-1
由於deep(EL)=deep(ER)+1所以E—>bf=0
2、當E—>bf=0時:deep(E)=deep(EL)+1,deep(ER)=deep(EL)
deep(C)=deep(E)-1=deep(EL)+1-1=deep(EL)可得B—>bf=0
deep(D)=deep(B)-2=deep(E)+1-2=deep(ER)+1+1-2=deep(ER)可得A—>bf=0
由於B—>bf=0,A—>bf=0所以E—>bf=0
3、當E—>bf=-1時:deep(E)=deep(ER)+1,deep(ER)=deep(EL)+1
deep(C)=deep(E)-1=deep(EL)+1+1-1=deep(EL)+1可得B—>bf=1
deep(D)=deep(B)-2=deep(E)+1-2=deep(ER)+1+1-2=deep(ER)可得A—>bf=0
由於deep(EL)=deep(ER)-1所以E—>bf=0
二、當出現圖2.2(b)(c)這兩種情況時,需進行右平衡處理:
1)當新結點插入到C的左子樹上時C—>bf=1,由此可知:deep(C)=deep(E)+1,deep(D)=deep(E)-1;由於插入新結點前A—>bf=-1,deep(B)=deep(C)-1則插入新節點后deep(B)=deep(C)-2;圖3.3.1為調整前的二叉樹,圖3.3.2為先對C樹進行右旋然后對A樹進行左旋后的AVL樹:
圖3.3.1 圖3.3.2
對比圖3.3.1和3.3.2可知調整后的新樹中A的右子樹發生了變化,C的左子樹發生了變化,E的左右子樹都發生了變化,其他結點都沒變,因此只需要重新算出A的平衡因子、B的平衡因子以及E的平衡因子即可證明調整后的樹是否為AVL樹。
此時由於調整后的A和C結點的平衡因子與E的左右子樹EL和ER有關,因此需要根據E的平衡因子的不同來進行分析:
由上面的分析可得到deep(B)=deep(C)-2,deep(C)=deep(E)+1,deep(D)=deep(E)-1
1、當E—>bf=1時:deep(E)=deep(EL)+1,deep(ER)=deep(EL)-1
deep(B)=deep(C)-2=deep(EL)+1+1-2=deep(EL)可得A—>bf=0
deep(D)=deep(E)-1=deep(ER)+1+1-1=deep(ER)+1可得C—>bf=-1
由於deep(EL)=deep(ER)+1所以E—>bf=0
2、當E—>bf=0時:deep(E)=deep(EL)+1,deep(ER)=deep(EL)
deep(B)=deep(E)+1-2=deep(EL)+1+1-2=deep(EL)可得A—>bf=0
deep(D)=deep(E)-1=deep(ER)+1-1=deep(ER)可得C—>bf=0
由於A—>bf=0,C—>bf=0所以E—>bf=0
3、當E—>bf=-1時:deep(E)=deep(ER)+1,deep(ER)=deep(EL)+1
deep(B)=deep(C)-2=deep(E)+1-2=deep(EL)+1+1+1-2=deep(EL)+1可得A—>bf=1
deep(D)=deep(E)-1=deep(ER)+1-1=deep(ER)可得C—>bf=0
由於deep(EL)=deep(ER)-1所以E—>bf=0
2)當新結點插入到C的右子樹上時C—>bf=-1,由此可知:deep(C)=deep(D)+1,deep(D)=deep(E)+1;由於插入新結點前A—>bf=-1,deep(B)=deep(C)-1則插入新節點后deep(B)=deep(C)-2;圖3.4.1為調整前的二叉樹,圖3.4.2為進行左旋后的AVL樹:
圖3.4.1 圖3.4.2
對比圖3.4.1和3.4.2可知調整后的新樹中A的右子樹發生了變化,C的左子樹發生了變化,其他結點都沒變,因此只需要重新算出A的平衡因子和C的平衡因子即可證明調整后的樹是否為AVL樹。
由上面的等式deep(B)=deep(C)-2,deep(C)=deep(D)+1,deep(D)=deep(E)+1
可以推出deep(B)=deep(C)-2=deep(D)+1-2=deep(E)+1+1-2=deep(E)可得出A—>bf=0
由A—>bf=0調整后deep(A)=deep(E)+1=deep(D)-1+1=deep(D)可得出C—>bf=0;
4.Java實現代碼:
1 package org.algorithms.tree; 2
3 import java.util.concurrent.ConcurrentLinkedQueue; 4
5
6 public class BalanceBiTree<T> { 7
8 private Node root; 9
10 private int size; 11
12 public void insert(T t){ 13 if(root==null){ 14 root = new Node(); 15 root.bf = 0; 16 root.data = t; 17 size++; 18 return; 19 } 20 addNode(root,t); 21 } 22
23 private boolean addNode(Node nd,T t){ 24 boolean taller = false; 25 Comparable<T> cp = (Comparable<T>)nd.data; 26 int i = cp.compareTo(t); 27 if(i==0){ 28 return false; 29 }else if(i>0){ 30 if(nd.lChild==null){ 31 Node child = new Node(); 32 child.bf = 0; 33 child.data = t; 34 child.parent = nd; 35 nd.lChild = child; 36 size++; 37 if(nd.bf==0){ 38 nd.bf = 1; 39 return true; 40 } 41 nd.bf = 0; 42 }else{ 43 taller = addNode(nd.lChild, t); 44 if(taller){ 45 if(nd.bf==1){ 46 leftBalance(nd); 47 taller = false; 48 }else if(nd.bf==0){ 49 nd.bf = 1; 50 taller = true; 51 }else{ 52 nd.bf = 0; 53 taller = false; 54 } 55 } 56 } 57 }else{ 58 if(nd.rChild==null){ 59 Node child = new Node(); 60 child.bf = 0; 61 child.data = t; 62 child.parent = nd; 63 nd.rChild = child; 64 size++; 65 if(nd.bf==0){ 66 nd.bf = -1; 67 return true; 68 } 69 nd.bf = 0; 70 }else{ 71 taller = addNode(nd.rChild, t); 72 if(taller){ 73 if(nd.bf==1){ 74 nd.bf = 0; 75 taller = false; 76 }else if(nd.bf==0){ 77 nd.bf = -1; 78 taller = true; 79 }else{ 80 rightBalance(nd); 81 taller = false; 82 } 83 } 84 } 85 } 86 return taller; 87 } 88
89 public int getSize(){ 90 return size; 91 } 92
93 private void leftBalance(Node nd){ 94 Node leftChild = nd.lChild; 95 if(leftChild.bf==1){ 96 nd.bf = 0; 97 leftChild.bf = 0; 98 rightRotate(nd); 99 }else if(leftChild.bf==-1){ 100 Node rd = leftChild.rChild; 101 switch (rd.bf) { 102 case 1: 103 leftChild.bf=0;nd.bf = -1; 104 break; 105 case 0: 106 leftChild.bf=0;nd.bf = 0; 107 break; 108 case -1: 109 leftChild.bf = 1;nd.bf = 0; 110 break; 111 } 112 rd.bf = 0 ; 113 leftRotate(leftChild); 114 rightRotate(nd); 115 } 116 } 117
118 private void rightBalance(Node nd){ 119 Node rightChild = nd.rChild; 120 if(rightChild.bf==1){ 121 Node ld = rightChild.lChild; 122 switch (ld.bf) { 123 case 1: 124 rightChild.bf= -1; nd.bf = 0; 125 break; 126 case 0: 127 rightChild.bf=0;nd.bf = 0; 128 break; 129 case -1: 130 rightChild.bf = 0;nd.bf = 1; 131 break; 132 } 133 ld.bf = 0 ; 134 rightRotate(rightChild); 135 leftRotate(nd); 136 }else if(rightChild.bf==-1){ 137 nd.bf = 0; 138 rightChild.bf = 0; 139 leftRotate(nd); 140 } 141
142 } 143
144 private void leftRotate(Node nd){ 145 Node top = nd.rChild; 146 nd.rChild = top.lChild; 147 if(top.lChild!=null) 148 top.lChild.parent = nd; 149 top.lChild = nd; 150 top.parent = nd.parent; 151 if(nd.parent!=null){ 152 if(nd.parent.lChild == nd) 153 nd.parent.lChild = top; 154 else
155 nd.parent.rChild = top; 156 }else{ 157 root = top; 158 } 159 nd.parent = top; 160 } 161
162 private void rightRotate(Node nd){ 163 Node top = nd.lChild; 164 nd.lChild = top.rChild; 165 if(top.rChild!=null) 166 top.rChild.parent = nd; 167 top.rChild = nd; 168 top.parent = nd.parent; 169 if(nd.parent!=null){ 170 if(nd.parent.lChild == nd) 171 nd.parent.lChild = top; 172 else
173 nd.parent.rChild = top; 174 }else{ 175 root = top; 176 } 177 nd.parent = top; 178 } 179
180 public void printTree(){ 181 ConcurrentLinkedQueue<Node> queue = new ConcurrentLinkedQueue<Node>(); 182 ConcurrentLinkedQueue<Node> tempQueue = new ConcurrentLinkedQueue<Node>(); 183 queue.add(root); 184 int offset= 0; 185 int counter=2; 186 for(int i=0;i<50;i++) 187 System.out.print(" "); 188 while(queue.peek()!=null){ 189 Node node = queue.poll(); 190 String side = "L"; 191 if(node.parent!=null&&node.parent.rChild==node) 192 side = "R"; 193 System.out.print(node.data+"("+(node.parent==null?"":node.parent.data)+" "+side+")"); 194 if(node.parent!=null&&node.parent.rChild!=node) 195 for(int i=0;i<counter;i++) 196 System.out.print(" "); 197 if(node.lChild!=null) 198 tempQueue.add(node.lChild); 199 if(node.rChild!=null) 200 tempQueue.add(node.rChild); 201 if(queue.isEmpty()){ 202 offset += 3; 203 //counter--;
204 copyQueue(tempQueue,queue); 205 System.out.println(); 206 for(int i=0;i<50-offset;i++) 207 System.out.print(" "); 208 } 209 } 210
211 } 212
213 private void copyQueue(ConcurrentLinkedQueue<Node> source, 214 ConcurrentLinkedQueue<Node> target){ 215 while(source.peek()!=null){ 216 target.add(source.poll()); 217 } 218 } 219
220 private class Node{ 221
222 public T data; 223
224 public Node lChild; 225
226 public Node rChild; 227
228 public Node parent; 229
230 public int bf; 231 } 232 }