平衡二叉搜索樹(Self-balancing binary search tree)又被稱爲AVL樹(有別於AVL算法),且具備如下性質:它是一 棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,而且左右兩個子樹都是一棵平衡二叉樹,同時,平衡二叉樹一定是二叉排序樹。java
高度差能夠用平衡因子bf來定義,咱們用左子樹的高度減去右子樹的高度來表示bf,即-1<|bf|<1。node
引入平衡二叉樹是因爲二叉排序樹,在某些狀況會致使樹的高度一直的增長,好比一組有序的數據,在查找或建立時遞歸層級會很深,致使方法棧容易溢出。算法
平衡二叉樹是經過旋轉來緩解樹高度增長過快的狀況。數據結構
先介紹下最小不平衡節點的概念:插入一個節點以後,距離這個插入節點最近的不平衡節點就是最小不平衡節點。就是說在遞歸插入節點後,開始回溯,碰到的第一個不平衡的節點就是最小不平衡節點。函數
當最小不平衡節點右子樹高則須要左旋,左子樹高則須要右旋(還有些狀況須要先對其左/右子樹旋轉)。this
思考:spa
一、既然旋轉是經過平衡因子|bf|>1來決定怎麼旋轉的,那麼在旋轉前這些平衡因子是何時賦值的呢?指針
二、旋轉以後,哪些節點須要調整?,平衡因子又改如何調整呢?code
下圖只列出左子樹高的幾種狀況,T表示最小不平衡節點,L表示其左子樹,LR表示L的右子樹,blog
爲了形象用EH(0),LH(1),RH(-1)分別表示某一節點 左右子樹相等、左子樹高、右子樹高三種狀況。
根據L節點的左右子樹高度差來肯定直接右旋仍是先左旋再右旋,由於L爲最小不平衡子樹的左子樹,故不會出現L.bf=EH的狀況。
1、L.bf=LH
右旋:
旋轉以後T.bf=L.bf=EH
2、L.bf=RH
先左旋再右旋:
當L的平衡因子爲-1時則須要先對L進行右旋,而後再對T進行左旋。根據LR的狀況再分爲下面三種(由於旋轉兩次,那麼最後最小不平衡子樹的根節點爲LR,而且LR.bf=EH)
一、 LR=EH
旋轉以後T.bf=L.bf=EH
二、 LR=LH
旋轉以後T.bf=RH, L.bf=EH
三、 LR=RH
旋轉以後T.bf=EH, L.bf=LH
我認爲網上最容易懂的C語言版代碼入下:
1. #include "stdio.h"
2. #include "stdlib.h"
3. #include "io.h"
4. #include "math.h"
5. #include "time.h"
6. 7. #define OK 1
8. #define ERROR 0
9. #define TRUE 1
10. #define FALSE 0
11. #define MAXSIZE 100 /* 存儲空間初始分配量 */
12. 13. typedef int Status; /* Status是函數的類型,其值是函數結果狀態代碼,如OK等 */
14. 15. 16. /* 二叉樹的二叉鏈表結點結構定義 */
17. typedef struct BiTNode /* 結點結構 */
18. { 19. int data; /* 結點數據 */
20. int bf; /* 結點的平衡因子 */
21. struct BiTNode *lchild, *rchild; /* 左右孩子指針 */
22. } BiTNode, *BiTree; 23. 24. 25. /* 對以p爲根的二叉排序樹做右旋處理, */
26. /* 處理以後p指向新的樹根結點,即旋轉處理以前的左子樹的根結點 */
27. void R_Rotate(BiTree *P) 28. { 29. BiTree L; 30. L=(*P)->lchild; /* L指向P的左子樹根結點 */
31. (*P)->lchild=L->rchild; /* L的右子樹掛接爲P的左子樹 */
32. L->rchild=(*P); 33. *P=L; /* P指向新的根結點 */
34. } 35. 36. /* 對以P爲根的二叉排序樹做左旋處理, */
37. /* 處理以後P指向新的樹根結點,即旋轉處理以前的右子樹的根結點0 */
38. void L_Rotate(BiTree *P) 39. { 40. BiTree R; 41. R=(*P)->rchild; /* R指向P的右子樹根結點 */
42. (*P)->rchild=R->lchild; /* R的左子樹掛接爲P的右子樹 */
43. R->lchild=(*P); 44. *P=R; /* P指向新的根結點 */
45. } 46. 47. #define LH +1 /* 左高 */
48. #define EH 0 /* 等高 */
49. #define RH -1 /* 右高 */
50. 51. /* 對以指針T所指結點爲根的二叉樹做左平衡旋轉處理 */
52. /* 本算法結束時,指針T指向新的根結點 */
53. void LeftBalance(BiTree *T) 54. { 55. BiTree L,Lr; 56. L=(*T)->lchild; /* L指向T的左子樹根結點 */
57. switch(L->bf) 58. { /* 檢查T的左子樹的平衡度,並做相應平衡處理 */
59. case LH: /* 新結點插入在T的左孩子的左子樹上,要做單右旋處理 */
60. (*T)->bf=L->bf=EH; 61. R_Rotate(T); 62. break; 63. case RH: /* 新結點插入在T的左孩子的右子樹上,要做雙旋處理 */
64. Lr=L->rchild; /* Lr指向T的左孩子的右子樹根 */
65. switch(Lr->bf) 66. { /* 修改T及其左孩子的平衡因子 */
67. case LH: (*T)->bf=RH; 68. L->bf=EH; 69. break; 70. case EH: (*T)->bf=L->bf=EH; 71. break; 72. case RH: (*T)->bf=EH; 73. L->bf=LH; 74. break; 75. } 76. Lr->bf=EH; 77. L_Rotate(&(*T)->lchild); /* 對T的左子樹做左旋平衡處理 */
78. R_Rotate(T); /* 對T做右旋平衡處理 */
79. } 80. } 81. 82. /* 對以指針T所指結點爲根的二叉樹做右平衡旋轉處理, */
83. /* 本算法結束時,指針T指向新的根結點 */
84. void RightBalance(BiTree *T) 85. { 86. BiTree R,Rl; 87. R=(*T)->rchild; /* R指向T的右子樹根結點 */
88. switch(R->bf) 89. { /* 檢查T的右子樹的平衡度,並做相應平衡處理 */
90. case RH: /* 新結點插入在T的右孩子的右子樹上,要做單左旋處理 */
91. (*T)->bf=R->bf=EH; 92. L_Rotate(T); 93. break; 94. case LH: /* 新結點插入在T的右孩子的左子樹上,要做雙旋處理 */
95. Rl=R->lchild; /* Rl指向T的右孩子的左子樹根 */
96. switch(Rl->bf) 97. { /* 修改T及其右孩子的平衡因子 */
98. case RH: (*T)->bf=LH; 99. R->bf=EH; 100. break; 101. case EH: (*T)->bf=R->bf=EH; 102. break; 103. case LH: (*T)->bf=EH; 104. R->bf=RH; 105. break; 106. } 107. Rl->bf=EH; 108. R_Rotate(&(*T)->rchild); /* 對T的右子樹做右旋平衡處理 */
109. L_Rotate(T); /* 對T做左旋平衡處理 */
110. } 111. } 112. 113. /* 若在平衡的二叉排序樹T中不存在和e有相同關鍵字的結點,則插入一個 */
114. /* 數據元素爲e的新結點,並返回1,不然返回0。若因插入而使二叉排序樹 */
115. /* 失去平衡,則做平衡旋轉處理,布爾變量taller反映T長高與否。 */
116. Status InsertAVL(BiTree *T,int e,Status *taller) 117. { 118. if(!*T) 119. { /* 插入新結點,樹「長高」,置taller爲TRUE */
120. *T=(BiTree)malloc(sizeof(BiTNode)); 121. (*T)->data=e; (*T)->lchild=(*T)->rchild=NULL; (*T)->bf=EH; 122. *taller=TRUE; 123. } 124. else
125. { 126. if (e==(*T)->data) 127. { /* 樹中已存在和e有相同關鍵字的結點則再也不插入 */
128. *taller=FALSE; return FALSE; 129. } 130. if (e<(*T)->data) 131. { /* 應繼續在T的左子樹中進行搜索 */
132. if(!InsertAVL(&(*T)->lchild,e,taller)) /* 未插入 */
133. return FALSE; 134. if(*taller) /* 已插入到T的左子樹中且左子樹「長高」 */
135. switch((*T)->bf) /* 檢查T的平衡度 */
136. { 137. case LH: /* 本來左子樹比右子樹高,須要做左平衡處理 */
138. LeftBalance(T); *taller=FALSE; break; 139. case EH: /* 本來左、右子樹等高,現因左子樹增高而使樹增高 */
140. (*T)->bf=LH; *taller=TRUE; break; 141. case RH: /* 本來右子樹比左子樹高,現左、右子樹等高 */
142. (*T)->bf=EH; *taller=FALSE; break; 143. } 144. } 145. else
146. { /* 應繼續在T的右子樹中進行搜索 */
147. if(!InsertAVL(&(*T)->rchild,e,taller)) /* 未插入 */
148. return FALSE; 149. if(*taller) /* 已插入到T的右子樹且右子樹「長高」 */
150. switch((*T)->bf) /* 檢查T的平衡度 */
151. { 152. case LH: /* 本來左子樹比右子樹高,現左、右子樹等高 */
153. (*T)->bf=EH; *taller=FALSE; break; 154. case EH: /* 本來左、右子樹等高,現因右子樹增高而使樹增高 */
155. (*T)->bf=RH; *taller=TRUE; break; 156. case RH: /* 本來右子樹比左子樹高,須要做右平衡處理 */
157. RightBalance(T); *taller=FALSE; break; 158. } 159. } 160. } 161. return TRUE; 162. } 163. 164. int main(void) 165. { 166. int i; 167. int a[10]={3,2,1,4,5,6,7,10,9,8}; 168. BiTree T=NULL; 169. Status taller; 170. for(i=0;i<10;i++) 171. { 172. InsertAVL(&T,a[i],&taller); 173. } 174. printf("本樣例建議斷點跟蹤查看平衡二叉樹結構"); 175. return 0; 176. }
首先總體看一遍InsertAVL函數代碼,結合上面的圖,我想以前兩個問題都會有答案了,再重複下。
一、既然旋轉是經過平衡因子|bf|>1來決定怎麼旋轉的,那麼在旋轉前這些平衡因子是何時賦值呢?
在元素插入以後,首先認爲該元素就是一個子樹,從無到有,故高度增長taller=true,而後開始回溯到上一個節點根據元素插入後,對其的三種影響來調整平衡因子,同時從新賦值taller。
二、旋轉以後,哪些節點須要調整?平衡因子又改如何調整呢?
由於平衡二叉樹自己也是排序樹,就上圖以左子樹高爲列,若能直接右旋,影響的節點有T、L,最後會爲T.bf=L.bf=EH。
若要先左旋再右旋,則根據LR的取值再分三種狀況(最後會變成LR爲根,LR.bf=EH)
一、 LR.bf=EH:旋轉後 T.bf=L.bf=EH
二、 LR.bf=LH:旋轉後 T.bf=RH,L.bf=EH
三、 LR.bf=RH:旋轉後 T.bf=EH,L.bf=LH
右子樹高的狀況同理。
要是你對java實現不興趣,能夠不用往下看了,數據結構主要是其思想,實現只須要根據語言特性來稍做改變。
java代碼:
思路跟上面代碼同樣(原諒我偷懶不寫註釋...),主要是多了個rootAVL和preNode。緣由是c語言函數傳值能夠直接傳指針,這樣對於參數的修改會反應到被調函數外面。而java都是值傳遞,方法內操做的只是引用的一個副本,他們指向的地址相同而已,只能修改引用所指向的內存,修改引用副本是不會影響引用自己的。
故須要單獨處理節點的前驅和根節點, rootAVL用來保存最後的根節點,由於每次插入都須要從根節點開始遞歸。
preNode表示每次回溯時的前面一個節點。
public class AVL { private boolean taller=false; private Node root =null; private static final int EH=0; private static final int LH=1; private static final int RH=-1; private class Node{ public int data; public Node leftChild; public Node rightChild; public int balanceFactor; public Node(int data){ this.data=data; balanceFactor=0; } } public Node RRotate(Node T){ Node temp=T.leftChild; T.leftChild=temp.rightChild; temp.rightChild=T; return temp; } public Node LRotate(Node T){ Node temp=T.rightChild; T.rightChild=temp.leftChild; temp.leftChild=T; return temp; } public Node leftBalance(Node node,Node preNode){ Node child=node.leftChild; Node root=null; switch (child.balanceFactor){ case LH: node.balanceFactor=child.balanceFactor=EH; root=RRotate(node); if(preNode!=null && node.data< preNode.data){ preNode.leftChild=root; } if(preNode!=null && node.data> preNode.data){ preNode.rightChild=root; } break; case RH: Node rchild=child.rightChild; switch (rchild.balanceFactor){ case EH: node.balanceFactor=child.balanceFactor=EH; break; case LH: node.balanceFactor=RH; child.balanceFactor=EH; break; case RH: node.balanceFactor=EH; child.balanceFactor=LH; break; default:break; } rchild.balanceFactor=EH; node.leftChild=LRotate(child); root=RRotate(node); if(preNode!=null && node.data<preNode.data){ preNode.leftChild=root; } if(preNode!=null && node.data>preNode.data){ preNode.rightChild=root; } break; default:break; } return root; } public Node rightBalance(Node node,Node preNode){ Node child=node.rightChild; Node root=null; switch (child.balanceFactor){ case RH: node.balanceFactor=child.balanceFactor=EH; root=LRotate(node); if(preNode!=null && node.data<preNode.data){ preNode.leftChild=root; } if(preNode!=null && node.data>preNode.data){ preNode.rightChild=root; } break; case LH: Node lchild=child.leftChild; switch (lchild.balanceFactor){ case EH: node.balanceFactor=child.balanceFactor=EH; break; case RH: node.balanceFactor=LH; child.balanceFactor=EH; break; case LH: node.balanceFactor=EH; child.balanceFactor=RH; break; default:break; } lchild.balanceFactor=EH; node.rightChild=RRotate(child); root=LRotate(node); if(preNode!=null && node.data<preNode.data){ preNode.leftChild=root; } if( preNode!=null && node.data>preNode.data){ preNode.rightChild=root; } break; default:break; } return root; } public boolean insertNode(int value){ return insertNode(root,value,null); } public boolean insertNode(Node node, int value, Node preNode){ if(node==null){ node=new Node(value); node.balanceFactor=EH; taller=true; if(preNode!=null && node.data< preNode.data){ preNode.leftChild=node; } if(preNode!=null && node.data> preNode.data){ preNode.rightChild=node; } root =node; return true; }else{ if(value==node.data){ root =node; return false; } if (value<node.data){ if (!insertNode(node.leftChild, value, node)) { root =node; return false; } if(taller){ switch (node.balanceFactor){ case EH:taller=true;node.balanceFactor=LH;break; case RH:taller=false;node.balanceFactor=EH;break; case LH: taller=false; node=leftBalance(node,preNode); if(preNode!=null){ node=preNode; } break; default:break; } } } if (value>node.data){ if (!insertNode(node.rightChild, value,node)) { root =node; return false; } if(taller){ switch (node.balanceFactor){ case EH:taller=true;node.balanceFactor=RH;break; case LH:taller=false;node.balanceFactor=EH;break; case RH: taller=false; node=rightBalance(node,preNode); if(preNode!=null){ node=preNode; } break; default:break; } } } } root =node; return true; } public void inorderTraversal(){ inorderTraversal(root); } public void inorderTraversal(Node root){ if(root!=null){ inorderTraversal(root.leftChild); System.out.println("節點:"+root.data+" 平衡因子:"+root.balanceFactor); inorderTraversal(root.rightChild); } return ; } public static void main(String[] args) { //int[] data={8,6,4}; //int[] data={8,6,9,5,7,3}; //int[] data={8,6,7}; //int[] data={8,5,9,4,6,7}; //int[] data={8,5,9,4,7,6}; int[] data={8,5,9,7,6}; AVL avl=new AVL(); for(int i=0;i<data.length;i++){ avl.insertNode(data[i]); } avl.inorderTraversal(); } }