大一就學了數據結構,當聽到AVL樹的時候就很蒙,也沒認真。最近開始認真看,那書上講的何止是蒙,講的我是莫名其妙。不知道爲什麼那麼多人喜歡嚴蔚敏版的數據結構,沒覺得好在哪裏,講的少,講的粗略,講的也是莫名其妙。可能是我的問題把,所以在網上搜了相關資料的,我一直認爲,一個理論要馬上接收,其實挺難的,我的做法是先不管那麼多,掌握基本的操作就行,在你每次用的過程中,總會關心到某些值得推敲的地方,這樣的拋磚引玉,顯得理論沒那麼的生硬;因此,我寫博客都是按照操作方法的標準寫的,至於理論,百科多如牛毛,寫了也不懂;後知後覺,似乎更加容易些。
僅是個人學習經驗,有誤請指教
這篇博客是我在網上看到的,他也是直接寫操作的,並且寫的挺明白的,所以我轉了下
http://hxraid.iteye.com/blog/609949
平衡二叉樹定義(AVL):
它或者是一顆空樹,或者具有以下性質的二叉樹:它的左子樹和右子樹的深度之差的絕對值不超過1,且它的左子樹和右子樹都是一顆平衡二叉樹。
最小不平衡子樹:指離插入節點最近且以平衡因子的絕對值大於1的節點作爲根的子樹。
平衡因子(bf):結點的左子樹的深度減去右子樹的深度,那麼顯然-1<=bf<=1;
插入操作
在平衡二叉樹中插入結點與二叉查找樹最大的不同在於要隨時保證插入後整棵二叉樹是平衡的。那麼調整不平衡樹的基本方法就是: 旋轉,基本思路都是轉換到左旋和右旋。
1) 右旋: 在最小平衡子樹根節點平衡因子>=2且在根節點的左孩子的左孩子插入元素,進行右旋
2) 左旋: 在最小平衡子樹根節點平衡因子>=-2且在根節點的右孩子的右孩子插入元素,進行左旋。
3) 右左:最小平衡子樹根節點(80)的右孩子(100)的左孩子(90)的子節點(95)插入新元素,先繞根節點的右孩子節點(100)右旋,再圍根節點(80)左旋
4) 左右:在最小平衡子樹根節點(80)的左孩子(50)的右孩子(70)的子節點插入新元素,先繞根節點的左孩子節點(50)右旋,再圍根節點(80)左旋
平衡二叉樹性能分析
平衡二叉樹的性能優勢:
很顯然,平衡二叉樹的優勢在於不會出現普通二叉查找樹的最差情況。其查找的時間複雜度爲O(logN)。
平衡二叉樹的缺陷:
(1) 很遺憾的是,爲了保證高度平衡,動態插入和刪除的代價也隨之增加
平衡二叉樹的插入操作代碼(平衡旋轉)
- package net.hr.algorithm.search;
- /**平衡因子枚舉類*/
- enum B
- alanceFactor{
- LH("左子樹高"),EH("左右等高"),RH("右子樹高");
- private String illustration="";
- private BalanceFactor(String s){
- this.illustration=s;
- }
- public String toString(){
- return this.illustration;
- }
- }
- /**
- * 平衡二叉樹結點
- */
- class AVLNode<E extends Comparable<E>>{
- /**結點關鍵字*/
- E key=null;
- /**結點的平衡因子*/
- BalanceFactor bFactor=BalanceFactor.EH;
- /**結點的直接父親*/
- AVLNode<E> parent=null;
- /**結點的左右孩子*/
- AVLNode<E> lchild,rchild=null;
- AVLNode(E k){
- this.key=k;
- }
- /**
- * 格式輸出結點
- */
- public String toString(){
- //String fomateStr="";
- //if(this.lchild==null)
- String lchildStr=(this.lchild==null)?"null":this.lchild.key.toString();
- String rchildStr=(this.rchild==null)?"null":this.rchild.key.toString();
- return this.key+"[lchild="+lchildStr+",rchild="+rchildStr+"]";
- }
- }
- /**
- * 平衡二叉查找樹
- * @author heartraid
- */
- public class AVL<E extends Comparable<E>> {
- /**樹根*/
- private AVLNode<E> root=null;
- /**當前樹是否變高*/
- public boolean isTaller=false;
- public AVL(){
- }
- public boolean insert(E key){
- System.out.print("插入["+key+"]:");
- if(key==null) return false;
- if(root==null){
- System.out.println("插入到樹根。");
- root=new AVLNode<E>(key);
- return true;
- }
- else{
- System.out.print("搜索路徑[");
- return insertAVL(key,root);
- }
- }
- private boolean insertAVL(E key,AVLNode<E> node){
- System.out.print(node.key+" —>");
- // 樹中存在相同的key,不需要插入
- if(node.key.compareTo(key)==0){
- System.out.println("]. 搜索有相同關鍵字,插入失敗");
- isTaller=false;
- return false;
- }
- else{
- //左子樹搜索
- if(node.key.compareTo(key)>0){
- //當前node的左孩子爲空,則插入到結點的做孩子並修改結點的平衡因子爲LH
- if(node.lchild==null){
- System.out.println("]. 插入到"+node.key+"的左孩子");
- AVLNode<E> newNode=new AVLNode<E>(key);
- node.lchild=newNode; //設置左孩子結點
- newNode.parent=node; //設置父親結點
- isTaller=true; //樹長高了
- }
- //左孩子不爲空,則繼續搜索下去
- else{
- insertAVL(key,node.lchild);
- }
- //當前如果樹長高了,說明是因爲左孩子的添加改變了平衡因子(左高)。
- if(isTaller){
- System.out.print(" 樹變化了,"+node.key+"的平衡因子變化");
- switch(node.bFactor){
- //原來結點平衡因子是LH(bf=1),則左高以後bf=2,因此需要做左平衡旋轉
- case LH: {
- System.out.println("[LH=1 ——> LH=2]. 出現了不平衡現象[左比右高2]");
- System.out.println(" ★ 以"+node.key+"爲根將樹進行左平衡處理");
- leftBalance(node);
- isTaller=false;
- break;
- }
- //原來結點平衡因子是EH(bf=0),則左高了以後bf=1,不需要平衡處理。
- case EH:{
- System.out.println("[EH=0 ——> LH=1]. 沒有不平衡現象");
- node.bFactor=BalanceFactor.LH;
- isTaller=true;
- break;
- }
- //原來結點平衡因子是RH(bf=-1),則左高以後bf=0,不需要平衡處理。
- case RH:{
- System.out.println("[RH=-1 ——> EH=0]. 沒有不平衡現象");
- node.bFactor=BalanceFactor.EH;
- isTaller=false;
- break;
- }
- }//end switch
- }//end if
- }//end if
- //右子樹搜索
- else{
- if(node.rchild==null){
- System.out.println("]. 插入到"+node.key+"的右孩子");
- AVLNode<E> newNode=new AVLNode<E>(key);
- node.rchild=newNode; //設置右孩子結點
- newNode.parent=node; //設置父親結點
- isTaller=true; //樹長高了
- }
- else{
- insertAVL(key,node.rchild);
- }
- //當前如果樹長高了,說明是因爲右孩子的添加改變了平衡因子(右高)。
- if(isTaller){
- System.out.print(" 樹變化了,"+node.key+"的平衡因子變化");
- switch(node.bFactor){
- //原來結點平衡因子是LH(bf=1),則右高以後bf=0,不需要平衡處理。
- case LH: {
- System.out.println("[LH=1 ——> EH=0]. 沒有不平衡現象");
- node.bFactor=BalanceFactor.EH;
- isTaller=false;
- break;
- }
- //原來結點平衡因子是EH(bf=0),則右高了以後bf=-1,不需要平衡處理。
- case EH:{
- System.out.println("[EH=0 ——> RH=-1]. 沒有不平衡現象");
- node.bFactor=BalanceFactor.RH;
- isTaller=true;
- break;
- }
- //原來結點平衡因子是RH(bf=-1),則右高以後bf=0,因此需要做右平衡旋轉。
- case RH:{
- System.out.println("[RH=-1 ——> RH=-2]. 出現了不平衡現象[左比右矮2]");
- rightBalance(node);
- isTaller=false;
- break;
- }
- }//end switch
- }//end if(isTaller)
- }//end else
- return true;
- }//end else
- }
- /**
- * 左平衡旋轉處理
- * 先對node的左子樹進行單左旋處理,在對node樹進行單右旋處理
- *
- * 100 100 90
- * / \ 左旋 / \ 右旋 / \
- * 80 120 ------> 90 120 ------> 80 100
- * / \ / / \ \
- * 60 90 80 60 85 120
- * / / \
- * 85 60 85
- *
- * @param node 需要做處理的子樹的根結點
- */
- private void leftBalance(AVLNode<E> node){
- // node.parent指向新的孩子結點
- AVLNode<E> lc=node.lchild;//lc指向node的左孩子結點
- switch(lc.bFactor){
- case LH:{ //新結點插入在node的左孩子的左子樹上,則需要單右旋處理
- System.out.println(" ┖ 對"+node.key+"進行單右旋轉處理");
- node.bFactor=lc.bFactor=BalanceFactor.EH;
- rRotate(node);
- break;
- }
- case RH:{ //新結點插入在node的左孩子的右子樹上,需要雙旋處理
- System.out.println(" ┖ 對"+node.key+"的左子樹進行單左旋轉處理,再對其本身樹進行單右循環處理");
- AVLNode<E> rd=lc.rchild; //rd指向node左孩子的右子樹根
- switch(rd.bFactor){ //修改node與其左孩子的平衡因子
- case LH:{
- node.bFactor=BalanceFactor.RH;
- lc.bFactor=BalanceFactor.EH;
- break;
- }
- case EH:{
- node.bFactor=lc.bFactor=BalanceFactor.EH;
- break;
- }
- case RH:{
- node.bFactor=BalanceFactor.EH;
- lc.bFactor=BalanceFactor.LH;
- break;
- }
- }//switch
- rd.bFactor=BalanceFactor.EH;
- lRotate(node.lchild);
- rRotate(node);
- break;
- }
- }
- }
- /**
- * 右平衡旋轉處理
- *
- * 80 80 85
- * / \ 右 旋 / \ 左 旋 / \
- * 60 100 ------> 60 85 -------> 80 100
- * / \ \ / / \
- * 85 120 100 60 90 120
- * \ / \
- * 90 90 120
- *
- * @param node
- */
- private void rightBalance(AVLNode<E> node){
- AVLNode<E> lc=node.rchild;//lc指向node的右孩子結點
- switch(lc.bFactor){
- case RH:{ //新結點插入在node的右孩子的右子樹上,則需要單左旋處理
- node.bFactor=lc.bFactor=BalanceFactor.EH;
- lRotate(node);
- break;
- }
- case LH:{ //新結點插入在node的右孩子的左子樹上,需要雙旋處理
- AVLNode<E> rd=lc.lchild; //rd指向node右孩子的左子樹根
- switch(rd.bFactor){ //修改node與其右孩子的平衡因子
- case LH:{
- node.bFactor=BalanceFactor.EH;
- lc.bFactor=BalanceFactor.RH;
- break;
- }
- case EH:{
- node.bFactor=lc.bFactor=BalanceFactor.EH;
- break;
- }
- case RH:{
- node.bFactor=BalanceFactor.LH;
- lc.bFactor=BalanceFactor.EH;
- break;
- }
- }//switch
- rd.bFactor=BalanceFactor.EH;
- rRotate(node.rchild);
- lRotate(node);
- break;
- }
- }
- }
- /**
- * 對以node爲根的子樹進行單右旋處理,處理後node.parent指向新的樹根,即旋轉之前
- * node的左孩子結點
- * 100<-node.parent 80<-node.parent
- * / / \
- * 80 ———> 60 100
- * / \ /
- * 60 85 85
- */
- private void rRotate(AVLNode<E> node){
- AVLNode<E> lc=node.lchild;//lc指向node的左孩子結點
- node.lchild=lc.rchild;
- lc.rchild=node;
- if(node.parent==null){
- root=lc;
- }
- else if(node.parent.lchild.key.compareTo(node.key)==0)
- node.parent.lchild=lc;
- else node.parent.rchild=lc;
- }
- /**
- * 對以node爲根的子樹進行單左旋處理,處理後node.parent指向新的樹根,即旋轉之前
- * node的右孩子結點
- * 100<-node.parent 110<-node.parent
- * \ / \
- * 110 ————> 100 120
- * / \ \
- * 105 120 105
- */
- private void lRotate(AVLNode<E> node){
- AVLNode<E> rc=node.rchild;//lc指向node的右孩子結點
- node.rchild=rc.lchild;
- rc.lchild=node;
- if(node.parent==null){
- root=rc;
- }
- else if(node.parent.lchild.key.compareTo(node.key)==0)
- node.parent.lchild=rc;
- else node.parent.rchild=rc;
- }
- /**
- * 得到BST根節點
- * @return BST根節點f
- */
- public AVLNode<E> getRoot(){
- return this.root;
- }
- /**
- * 遞歸前序遍歷樹
- */
- public void preOrderTraverse(AVLNode<E> node){
- if(node!=null){
- System.out.println(node);
- preOrderTraverse(node.lchild);
- preOrderTraverse(node.rchild);
- }
- }
- /**
- * 測試
- * @param args
- */
- public static void main(String[] args) {
- AVL<Integer> avl=new AVL<Integer>();
- avl.insert(new Integer(80));
- avl.insert(new Integer(60));
- avl.insert(new Integer(90));
- avl.insert(new Integer(85));
- avl.insert(new Integer(120));
- avl.insert(new Integer(100));
- System.out.println("前序遍歷AVL:");
- avl.preOrderTraverse(avl.getRoot());
- }
- }
相關問題1:N層平衡二叉樹至少多少個結點
假設F(N)表示N層平衡二叉樹的結點個數,則F[1]=1,F[2]=2。而F(N)=F(N-2)+F(N-1)+1
爲什麼呢?我們可以這樣考慮,假設現在又一個(N-2)層和(N-1)層的最少結點平衡二叉樹。要構造一棵N層的平衡二叉樹,則只需加入一個根節點,其左右子樹分別(N-2)層和(N-1)層的樹即可 。由於兩個子樹都是最少結點的,所有N層的也是最少結點的。