AVL樹(二叉平衡樹)詳解與實現

AVL樹概念

前面學習二叉查找樹二叉樹的各類遍歷,可是其查找效率不穩定(斜樹),而二叉平衡樹的用途更多。查找相比穩定不少。(歡迎關注數據結構專欄)java

  • AVL樹是帶有平衡條件的二叉查找樹。這個平衡條件必需要容易保持。並且要保證它的深度是O(logN).
  • AVL的條件是左右樹的高度差(平衡因子)不大於1;而且它的每一個子樹也都是平衡二叉樹。
  • 對於平衡二叉樹的最小個數,n0=0;n1=1;nk=n(k-1)+n(k-2)+1;(求法能夠類比斐波那契!)

難點:AVL是一顆二叉排序樹,用什麼樣的規則或者規律讓它可以在複雜度不過高的狀況下實現動態平衡呢?
在這裏插入圖片描述node

不平衡概況

在這裏插入圖片描述
若是簡單的以單節點看,大體有上面四種情形,而且他們的最後結果也是有的有所相近。只是:上下會變更。該在左面的還在左面,改在右面的還在右面
在這裏插入圖片描述
這只是針對在底部,對於可能出現的平衡要首先搞清楚:
在這裏插入圖片描述
因此針對四種不平衡,可能出如今底部,也可能出如今頭,也可能出如今某個中間節點致使不平衡。 而咱們只須要研究其首次不平衡點,解決以後整棵樹即繼續平衡。固然,在實際解決確定會帶上遞歸的思想解決問題。算法

# 四種平衡旋轉方式

RR平衡旋轉(左單旋轉)

在這裏插入圖片描述
出現這種狀況的緣由是節點的右側的右側較深這時候不平衡節點須要左旋。再細看過程。後端

  • 再左旋的過程當中,root(oldroot)節點下沉,中間節點(newroot)上浮.而其中中間節點(newroot)的右側依然不變。
  • 它上浮左側因此須要指向根節點(oldroot)(畢竟一棵樹)。可是這樣newroot原來左側節點H空缺。而咱們須要仍然讓整個樹完整而且知足二叉排序樹的規則
  • 而恰好原本oldroot右側指向newroot變成oldroot被newroot左側指向。因此oldroot右側空缺,恰好這個位置知足在oldroot的右側。在newroot的左側。.因此咱們將H插入在這個位置。
  • 其中H可能爲NULL。不過不影響操做!
    在這裏插入圖片描述
    而左旋的代碼能夠表示爲:
private node getRRbanlance(node oldroot) {//右右深,須要左旋
    // TODO Auto-generated method stub
    node newroot=oldroot.right;
    oldroot.right=newroot.left;
    newroot.left=oldroot;
    oldroot.height=Math.max(getHeight(oldroot.left),getHeight(oldroot.right))+1;
    newroot.height=Math.max(getHeight(newroot.left),getHeight(newroot.right))+1;//原來的root的高度須要重新計算
    return newroot;     
}

LL平衡旋轉(右單旋轉)

而右旋和左旋相反,可是思路相同,根據上述進行替換便可!
在這裏插入圖片描述
代碼:數據結構

private node getLLbanlance(node oldroot) {//LL小,須要右旋轉
    // TODO Auto-generated method stub
    node newroot=oldroot.left;
    oldroot.left=newroot.right;
    newroot.right=oldroot;
    oldroot.height=Math.max(getHeight(oldroot.left),getHeight(oldroot.right))+1;
    newroot.height=Math.max(getHeight(newroot.left),getHeight(newroot.right))+1;//原來的root的高度須要重新金酸  
    return newroot; 
}

RL平衡旋轉(先右後左雙旋轉)

產生不平衡的條件緣由是:學習

  • root節點右側左側節點的深度高些,使得與左側的差大於1.這個與咱們前面看到的左旋右旋不一樣的是由於它的結構不能直接變一下就能夠完成。
  • 由於對於右左結構,中間的最大,兩側的最小。可是下面的比上面大(下面在上面右側)因此若是平衡的話,那麼右左的R.L應該在中間,而R應該在右側。原來的root在左側。
  • 因此節點的變化浮動比較大,並且須要妥善處理各個子節點的移動使其知足二叉排序樹的性質!
  • 期間考慮樹高度變化便可!

這種雙旋轉其實也很簡單。不要被外表唬住。基於前面的單旋轉,雙旋轉有兩種具體邏輯思路
思路1:兩次旋轉RR,LL
在這裏插入圖片描述
根據上圖所圈的,先對底部使得底部的大小關係變化,使其在知足二叉平衡樹的條件下還知足RR結構的二叉樹。因此只須要對右節點R先進行右旋,再對ROOT進行左旋便可。
思路2:直接分析
根據初始和結果的狀態,而後分析各個節點變化順序。手動操做這些節點便可!測試

  • 首先根據ROOT,R,R.L三個節點變化。R.L確定要在最頂層。左右分別指向ROOT和R。那麼這其中R.left,ROOT.right發生變化(原來分別是R,L和R)暫時爲空。而恰好根據左右大小關係能夠補上R.L的左右節點
  • 這樣思考整棵樹也能夠完成平衡,可是要考慮樹的高度變化
    在這裏插入圖片描述
    代碼爲:(註釋部分爲方案1)
private node getRLbanlance(node oldroot) {//右左深 
//      node newroot=oldroot.right.left;
//      oldroot.right.left=newroot.right;
//      newroot.right=oldroot.right;
//      oldroot.right=newroot.left; 
//      newroot.left=oldroot;
//      oldroot.height=Math.max(getHeight(oldroot.left),getHeight(oldroot.right))+1;
//      newroot.right.height=Math.max(getHeight(newroot.right.left),getHeight(newroot.right.right))+1;
//      newroot.height=Math.max(getHeight(oldroot.left),getHeight(newroot.right))+1;//原來的root的高度須要重新金酸  
    oldroot.right =getLLbanlance(oldroot.right);
    oldroot.height=Math.max(getHeight(oldroot.left), getHeight(oldroot.right))+1;
    return getRRbanlance(oldroot);
        
    }

LR平衡旋轉(先左後右單旋轉)

根據上述RL修改便可
在這裏插入圖片描述.net

private node getLRbanlance(node oldroot) {
    oldroot.left =getRRbanlance(oldroot.left);
    oldroot.height=Math.max(getHeight(oldroot.left), getHeight(oldroot.right))+1;
    return getLLbanlance(oldroot);
        
    }

java代碼實現

  • 首先對於節點多個height屬性。用於計算高度(平衡因子)
  • 插入是遞歸插入。遞歸一個來回的過程,去的過程進行插入。回的過程進行高度更新。和檢查是否平衡。不要寫全局遞歸計算高度,效率過低下。事實上高度變化只和插入和平衡有關,仔細考慮即不會有疏漏!
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述code

    總結

    測試狀況:
    在這裏插入圖片描述
  • AVL的理解須要時間,固然筆者的AVL本身寫的可能有些疏漏,若是有問題還請各位一塊兒探討
  • 固然,除了插入,AVL還有刪除等其餘操做,(原理類似。刪除後平衡)有興趣能夠一塊兒研究。
  • 若是須要源碼還請關注筆者公衆號:公衆號查看相關專題文章!
  • 若是對後端、爬蟲、數據結構算法等感性趣歡迎關注個人我的公衆號交流:bigsai(回覆數據結構、爬蟲、java等有精心準備資料一份!)
    blog

相關文章
相關標籤/搜索