數據結構之各種樹


導讀

  文本總結了數據結構中常見的各種樹,前面的文章中我們介紹過樹和二叉樹,比如用於實現平衡二叉樹的AVL樹和紅黑樹、B樹、B-樹、B+樹、B*樹、R樹及字典樹Trie樹。

目錄

  1、平衡二叉樹之AVL樹

    2、平衡二叉樹之紅黑樹

 

一、平衡二叉樹之AVL樹

  參考博客:https://www.cnblogs.com/QG-whz/p/5167238.html#_label1

  學習AVL的的關鍵在於使用旋轉操作進行失衡處理

  1、定義

  AVL樹是最先發明的自平衡二叉查找樹。所以AVL樹是一種二叉平衡樹,即AVL樹是二叉平衡樹的一種實現。

  ps:AVL樹既然是二叉平衡樹,那么必須滿足:1)是二次排序樹;2)左右子樹相對高度差不大於1。

  2、相關概念

  平衡因子:將二叉樹上結點的左子樹高度減去右子樹高度的值稱為該結點的平衡因子BF(Balance Factor)。

  最小不平衡子樹:距離插入結點最近的,且以平衡因子絕對值大於1的結點為根的子樹。

  

  在圖三中,左邊二叉樹的結點45的BF = 1,插入結點43后,結點45的BF = 2。結點45是距離插入點43最近的BF不在[-1,1]范圍內的結點,因此以結點45為根的子樹為最小不平衡子樹。

  3、AVL樹的實現

  1)結點結構

struct AVLTreeNode<T> {  
 T key;//結點數據
    int height;//結點高度(用於計算父結點的平衡因子)
    AVLTreeNode<T> lchild;//左孩子指針
    AVLTreeNode<T> rchild;//右孩子指針
};

   ps:這里結點高可以替換為平衡因子。

  2)AVL樹的抽象數據結構

template<typename T>
class AVLTree
{
public:
    AVLTree();            //構造函數
    ~AVLTree();            //析構函數
 
    void preOrder();    //前序遍歷AVL樹
    void InOrder();        //中序遍歷AVL樹   
    void postOrder();    //后序遍歷AVL樹
 
    void print();        //打印AVL樹
    void destory();        //銷毀AVL樹
 
    void insert(T key);    //插入指定值的節點
    void remove(T key);    //移除指定值的節點
 
    AVLTreeNode<T>* search_recurse(T key);    //利用遞歸算法進行指定值的查找
    AVLTreeNode<T>* search_iterator(T key);    //利用迭代算法進行指定值的查找
    T minimum();        //返回AVL中的最小值
    T maximum();        //返回AVL中的最大值
 
    int height();        //返回樹的高度
 
private:
    AVLTreeNode<T>* root;    //AVL樹的根節點
 
private:
    void preOrder(AVLTreeNode<T>* pnode) const;
    void inOrder(AVLTreeNode<T>* pnode) const;
    void postOrder(AVLTreeNode<T>* pnode) const;
 
    void print(AVLTreeNode<T>* pnode,T key, int direction) const;
    void destory(AVLTreeNode<T>* & pnode);
 
    AVLTreeNode<T>* insert(AVLTreeNode<T>* &pnode, T key);       
    AVLTreeNode<T>* remove(AVLTreeNode<T>* & pnode, AVLTreeNode<T>* pdel); //刪除AVL樹中節點pdel,並返回被刪除的節點
 
    AVLTreeNode<T>* minimum(AVLTreeNode<T>*pnode)const;
    AVLTreeNode<T>* maximum(AVLTreeNode<T>*pnode)const;
 
    AVLTreeNode<T>* search_recurse(AVLTreeNode<T>* pnode, T key) const;
    AVLTreeNode<T>* search_iterator(AVLTreeNode<T>* pnode, T key) const;
 
    AVLTreeNode<T>* leftRotation(AVLTreeNode<T>* pnode);        //單旋:左旋操作
    AVLTreeNode<T>* rightRotation(AVLTreeNode<T>* pnode);        //單旋:右旋操作
    AVLTreeNode<T>* leftRightRotation(AVLTreeNode<T>* pnode);    //雙旋:先左旋后右旋操作
    AVLTreeNode<T>* rightLeftRotation(AVLTreeNode<T>* pnode);    //雙旋:先右旋后左旋操作
 
};

  4、AVL樹的操作

  AVL樹具有一般二叉樹的查找、插入和刪除等基本操作,但AVL樹作為自平衡二叉查找樹,需要保證其平衡因子不大於1,而插入或刪除操作都可能會打破樹的平衡,所以AVL樹的特殊操作是進行旋轉操作,以此來調整樹的不平衡子樹。

  1)旋轉操作

  為了方便說明,假設我們要為數組a[]={4,5,6,3,2,8,7,0,1}構建一棵AVL樹。

  情況一:左單旋轉

  首先插入{4,5,6},在插入元素6后出現不平衡的情況:

  

  當我們在右子樹插入右孩子導致AVL失衡時,我們需要進行單左旋調整。旋轉圍繞最小失衡子樹的根節點進行。
  在刪除新節點時也有可能會出現需要單左旋的情況。
  左旋參考代碼如下:

/*左旋轉操作*/
/*pnode為最小失衡子樹的根節點*/
/*返回旋轉后的根節點*/
template<typename T>
AVLTreeNode<T>* AVLTree<T>::leftRotation(AVLTreeNode<T>* proot)
{
    AVLTreeNode<T>* prchild = proot->rchild;
    proot->rchild = prchild->lchild;
    prchild->lchild = proot;
 
    proot->height = max(height(proot->lchild),height(proot->rchild))+1;     //更新節點的高度值
    prchild->height = max(height(prchild->lchild), height(prchild->rchild)) + 1; //更新節點的高度值
 
    return prchild;                    
};

  情況二:右單旋轉

  我們繼續插入元素{3,2},此時二叉樹為:

  

  此時的插入情況是“在左子樹上插入左孩子導致AVL樹失衡”,我們需要進行單右旋調整。
    單右旋參考代碼為:

/*右旋轉操作*/
/*pnode為最小失衡子樹的根節點*/
/*返回旋轉后的根節點*/
template <typename  T>
AVLTreeNode<T>* AVLTree<T>::rightRotation(AVLTreeNode<T>*proot)
{
    AVLTreeNode<T>* plchild = proot->lchild;
    proot->lchild = plchild->rchild;
    plchild->rchild = proot;
 
    proot->height = max(height(proot->lchild), height(proot->rchild)) + 1;     //更新節點的高度值
    plchild->height = max(height(plchild->lchild), height(plchild->rchild)) + 1; //更新節點的高度值
 
    return plchild;
};

  情況三:先左旋后右旋

  需要進行兩次旋轉的原因是第一次旋轉后,AVL樹仍舊處於不平衡的狀態,第二次旋轉再次進行調整。
  我們繼續插入元素{8,7}:

  

  這種情況,總結起來就是“在右子樹上插入左孩子導致AVL樹失衡",此時我們需要進行先右旋后左旋的調整。

  情況四:先左旋后右旋

  根據對稱性原理,當我們“在左子樹上插入右孩子導致AVL樹失衡",此時我們需要進行先左旋后右旋的調整。如果你不理解接着看圖。
  我們接着插入節點{0,1}:

  

  總結:四種失衡調整

  

  2)其他操作

  其他操作如插入、刪除、查找、遍歷與一般二叉樹的操作類似,不同之處在於如果插入或刪除操作導致樹失衡,需要進行旋轉操作進行恢復平衡。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM