C++實現平衡二叉樹


1.概念

平衡二叉樹(AVL Tree)首先要滿足二叉樹的定義,如下

  • 二叉排序樹或者是一棵空樹,或者是具有下列性質的二叉樹:
  • 若左子樹不空,則左子樹上所有結點的值均小於它的根結點的值;
  • 若右子樹不空,則右子樹上所有結點的值均大於它的根結點的值;
  • 左、右子樹也分別為二叉排序樹;
  • 沒有鍵值相等的節點。
  • 平衡度是左子樹高度減去右子樹高度,平衡度只能是-1,+1,0
下圖給出了一個非平衡二叉排序樹和平衡二叉排序樹
說明:結點上方的數字是平衡度(平衡因子),a圖9結點左子樹比右子樹高2,不滿足AVL Tree的定義,所以是非平衡樹
b圖所有結點度數絕對值沒有超過1,所以滿足平衡樹的定義,是平衡樹
  • 危機結點是指平衡度為1,有可能破壞平衡樹的結點
  • 左改組是指新結點插入到危機結點的左樹下
  • 右改組是指新新結點插入到危機結點的右子樹下
  • LL是新結點插入在危機節點的左子樹的左子樹上
  • LR是新結點插入在危機節點的左子樹的右子樹上
  • RR是新結點插入在危機節點的右子樹的右子樹上
  • RL是新結點插入在危機節點的右子樹的左子樹上
 

2.代碼實現

當前樹的結構

            11
          /   \
        7       15
       / \    /  \
      3      9  14      18
     / \     /    / \
    1   5    12   16     20
                     /
                   26

2.1 定義平衡樹結點:

平衡因子可以是右子樹高度減去左子樹高度,不同的教材定義不一樣,我這里按照左樹-右樹來做

template<class K, class V>
struct AVLTreeNode
{
    K _key;                //樹權值
    V _value;
    int  _bf;            //平衡因子 -1,0,1        (每個節點的平衡因子等於左子樹的高度減去右子樹的高度) 
                        //有的教材定義平衡度是左子樹高度減去右子樹,都是可以的
    AVLTreeNode<K, V>* _parent;    //指向父節點的指針
    AVLTreeNode<K, V>* _left;            //指向左孩子的指針
    AVLTreeNode<K, V>* _right;        //指向右孩子的指針

    AVLTreeNode(const K& key = K(), const V& value = V())
        :_key(key)
        , _value(value)
        , _bf(0)
        , _parent(NULL)
        , _left(NULL)
        , _right(NULL)
    {}
};

2.2 左改組圖解

左右改組是為了方便我們插入刪除的時候保持二叉樹平衡而引入的概念

左改組LL型和LR(a),LR(b),LR(c)型圖解

2.3 左改組LL型

首先聲明一個構造的左子樹,subL其實就是危機結點,subLR是危機節點的右子樹,ppNode是祖先節點

構建parent子樹,將parent和subL連接起來

如果祖先結點為空,將當前結點subL置為根節點,請參見上圖(a‘)的情況,B是危機結點,調整過后變成了根節點

否則祖父結點賦給subL的父結點,判斷父節點是否是祖先結點的左子樹,是的話,用構造的左子樹替代之

不是就用subL替代祖先節點的右子樹

//左改組LL型
template<class K, class V>
void AVLTree<K, V>::_RotateLL(AVLTreeNode<K, V>*&  parent)
{
    AVLTreeNode<K, V>* subL = parent->_left; //構造的左子樹
    AVLTreeNode<K, V>* subLR = subL->_right;//subL的右子樹
    AVLTreeNode<K, V>* ppNode = parent->_parent;//標記祖先節點
    //1.構建parent子樹 將parent和subLR鏈接起來
    parent->_left = subLR;
    if (subLR) subLR->_parent = parent;
    //2.構建subL子樹 將subL與parent鏈接起來
    subL->_right = parent;
    parent->_parent = subL;
    //3.將祖先節點與subL鏈接起來
    if (ppNode == NULL)
    {    //如果祖先為NULL,說明當前subL節點為根節點
        subL->_parent = NULL;
        _root = subL;
    }
    else
    {
        subL->_parent = ppNode;
        if (ppNode->_left == parent)
            ppNode->_left = subL;
        else if (ppNode->_right == parent)
            ppNode->_right = subL;
    }
    //4.重置平衡因子
    parent->_bf = 0;
    subL->_bf = 0;
    //5.更新subL為當前父節點
    parent = subL;
}

2.4 左改組LR(a)、LR(b)和LR(c)型

pNode是當前父節點,subR是構造的右子樹,subLR是subR的左子樹

對當前父節點LL左改組,再右改組

根據平衡因子判斷是LR什么類型,請參見上圖圖解(b),(c),(d)的情況

//左改組LR型
template<class K, class V>
void AVLTree<K, V>::_RotateLR(AVLTreeNode<K, V>*&  parent)
{
    AVLTreeNode<K, V>* pNode = parent;
    AVLTreeNode<K, V>* subR = parent->_right;
    AVLTreeNode<K, V>* subLR = subR->_left;
    int bf = subLR->_bf;

    _RotateLL(parent->_right);
    _RotateRR(parent);
    //LR(b)型
    if (bf == -1)
    {
        pNode->_bf = 0;
        subR->_bf = 1;
    }
    //LR(a)型
    else if (bf == 1)
    {
        pNode->_bf = -1;
        subR->_bf = 0;
    }
    //LR(c)型
    else
    {
        pNode->_bf = 0;
        subR->_bf = 0;
    }
}

 右改組和左改組鏡像對稱,反過來就行了

2.5 插入函數

AVL樹是空,將當前結點直接置為根節點

AVL樹在滿足平衡度的要求下,和二叉排序樹一致,key小於當前結點,轉到當前結點左子樹,key大於當前結點,轉到當前結點右子樹

將parent的左子樹賦予當前結點,更新平衡因子,_bf++

將parent的右子樹賦予當前結點,更新平衡因子,_bf--

如果合法,即平衡因子=0,終止當前循環

如果當前結點是危機結點,即平衡度絕對值等於1,當前結點往上回溯,變成父節點,繼續檢查它的平衡度

接下來是平衡異常的情況,父結點平衡度為2,當前結點(危機結點)平衡度為1,進入左改組LL,LL介紹參考2.2 左改組LL

當前結點平衡度為-1,進入左改組LR,LR介紹參考2.3 左改組LR

右改組的情況類似

template<class K, class V>
bool AVLTree<K, V>::Insert(const K& key, const V& value)
{
    //1.空樹
    if (_root == NULL)
    {
        _root = new AVLTreeNode<K, V>(key, value);
        return true;
    }

    //2.AVL樹不為NULL
    AVLTreeNode<K, V>* parent = NULL;
    AVLTreeNode<K, V>* cur = _root;
    //找到數據插入位置
    while (cur)
    {
        if (cur->_key < key)
        {
            parent = cur;
            cur = cur->_right;
        }
        else if (cur->_key > key)
        {
            parent = cur;
            cur = cur->_left;
        }
        else
        {
            return false;
        }
    }
    //插入數據
    cur = new AVLTreeNode<K, V>(key, value);
    cur->_parent = parent;
    if (parent->_key > key)
        parent->_left = cur;
    else
        parent->_right = cur;

    while (parent)
    {
        //更新平衡因子
        if (cur == parent->_left)
            parent->_bf++;
        else if (cur == parent->_right)
            parent->_bf--;

        //檢驗平衡因子是否合法
        if (parent->_bf == 0)
            break;
        else if (parent->_bf == -1 || parent->_bf == 1)
        {    // 回溯上升 更新祖父節點的平衡因子並檢驗合法性
            cur = parent;
            parent = cur->_parent;
        }
        //    2 -2 平衡因子不合法 需要進行旋轉 降低高度
        else   
        {
            if (parent->_bf == -2)
            {
                if (cur->_bf == -1)
                    _RotateRR(parent);
                else
                    _RotateLR(parent);
            }
            else if (parent->_bf == 2)
            {
                if (cur->_bf == 1)
                    _RotateLL(parent);
                else
                    _RotateRL(parent);
            }
            break;
        }
    }
}

 2.6 遍歷方法

//中序遍歷
template<class K, class V>
void AVLTree<K, V>::_InOrder(AVLTreeNode<K, V>* root)
{
    if (root == NULL)
        return;
    _InOrder(root->_left);
    cout << root->_key << " ";
    _InOrder(root->_right);
}
//前序遍歷
template<class K, class V>
void AVLTree<K, V>::_PreOrder(AVLTreeNode<K, V>* root)
{
    if (root == NULL)
        return;
    cout << root->_key << " ";
    _PreOrder(root->_left);
    _PreOrder(root->_right);
}
//后序遍歷
template<class K, class V>
void AVLTree<K, V>::_PostOrder(AVLTreeNode<K, V>* root)
{
    if (root == NULL)
        return;
    _PostOrder(root->_left);
    _PostOrder(root->_right);
    cout << root->_key << " ";
}

3.運行和源碼

運行效果如下

源代碼:https://github.com/cjy513203427/C_Program_Base/tree/master/61.%E5%B9%B3%E8%A1%A1%E4%BA%8C%E5%8F%89%E6%A0%91/61.%E5%B9%B3%E8%A1%A1%E4%BA%8C%E5%8F%89%E6%A0%91

 


免責聲明!

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



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