搜索結構之AVL樹

AVL樹

概念
搜索二叉樹雖然降低了查找的效率,但是如果數據有序或者接近有序,那麼搜索二叉樹就會退化成單枝樹,搜索的時間複雜度就會變成O(n),極大的降低了效率,因此我們引入了AVL樹,在向樹中插入元素的時候,能保證每一個節點的左右子樹的高度不會相差1,從而降低了樹的高度,提高了查找的效率。
2.我們把具有一下性質的樹稱之爲AVL樹:

1. 首先它是二叉搜索樹。
2. 二叉樹每個節點的左子樹和右子樹都是AVL樹。
3. 每一個節點的平衡因子絕對值不超過1(平衡因子:當前節點右子樹高度減去左子樹高度)
這裏寫圖片描述
這樣就可以降低樹的高度,使得AVL樹的查找時間複雜度在O(log(n));


AVL樹的插入操作
插入操作很有可能導致AVL樹不滿足性質,所以我們需要旋轉操作來使得樹滿足性質。
具體插入過程:

  1. 當樹是空樹的時候,直接創建節點,此節點爲根節點,滿足性質
  2. 當樹非空時,先查找當前節點是否在樹中存在,若存在,插入失敗,直接返回,如果不存在,找到插入位置,插入節點,更新平衡因子,調整樹。
    新節點pcur的平衡因子爲0,它的雙親的平衡因子有三種情況:
    1.雙親parent的平衡因子爲0
    這種情況就是在parent的較矮的子樹上插入,那麼parent的平衡因子爲0,parent平衡,從parent到根節點的路徑上的節點的平衡因子都沒有發生改變,因此樹滿足AVL樹性質,不需調整。
    2.雙親的parent的平衡因子絕對值爲1
    parent節點平衡因子在插入之前爲0,插入之後以parent爲根的樹平衡沒有被破壞,但是子樹的高度增加了,向上回溯查看節點平衡因子。
    3.在上述2更新完成後,如果parent平衡因子的絕對值爲2,新節點在較高子樹上進行插入,那麼就必須要進行平衡處理:
    如果parent->_buf是2,說明右子樹高,設parent的右子樹爲subR,那麼
    當subR->_buf爲1時候,執行左單旋轉:
    當subR->buf爲-1的時候,執行先右後左雙旋;
    如果pparent->_buf是-2,說明左子樹高,折parent的左子樹是subL,那麼
    當subL->buf是-1的時候,執行右單旋;
    當subR->buf是1的時候,執行先左後右雙旋;
    旋轉之後parent爲跟的子樹高度降低,不需要向上進行回溯。

左單旋
插入節點位於較高右子樹的右側
這裏寫圖片描述

void RotateL(pNode pParent)
    {
        pNode subR = pParent->_pRight;
        pNode subRL = pParent->_pRight->_pLeft;
        pParent->_pRight = subRL;
        if (subRL)
            subRL->_pParent = pParent;
        subR->_pLeft = pParent;
        pNode pparent = pParent->_pParent;
        pParent->_pParent = subR;
        subR->_pParent = pparent;
        if (pParent == _pRoot)
        {
            _pRoot = subR;
        }
        else
        {
            if (pparent->_pLeft == pParent)
                pparent->_pLeft = subR;
            else
                pparent->_pRight = subR;
        }
        pParent->_buf = subR->_buf = 0;
    }

右單旋
插入節點位於較高左子樹的左側
這裏寫圖片描述

void RotateR(pNode pParent)
    {
        pNode subL = pParent->_pLeft;
        pNode subLR = pParent->_pLeft->_pRight;
        pParent->_pLeft = subLR;
        if (subLR)
            subLR->_pParent = pParent;
        subL->_pRight = pParent;
        pNode pparent = pParent->_pParent;
        pParent->_pParent = subL;
        subL->_pParent = pparent;
        if (pParent == _pRoot)
            _pRoot = subL;
        else
        {
            if (pparent->_pLeft == pParent)
                pparent->_pLeft = subL;
            else
                pparent->_pRight = subL;
        }
        pParent->_buf = subL->_buf = 0;
    }

右左雙旋
插入節點位於較高右子樹的左側
這裏寫圖片描述

    void RotateRL(pNode pParent)
    {
        pNode subR = pParent->_pRight;
        pNode subRL = NULL;
        if (subR != NULL)
            subRL = subR->_pLeft;
        RotateR(subR);
        RotateL(pParent);
        if (subRL->_buf == 1)
            pParent->_buf = -1;
        else if (subRL->_buf == -1)
            pParent->_buf = 0;
    }

左右雙旋
插入節點位於較高左子樹的右側
這裏寫圖片描述

void RotateLR(pNode pParent)
    {
        pNode subL = pParent->_pLeft;
        pNode subLR = NULL;
        if (subL != NULL)
            subLR = subL->_pRight;
        RotateL(subL);
        RotateR(pParent);
        if (subLR->_buf == 1)
            pParent->_buf = 0;
        else if (subLR->_buf == -1)
            pParent->_buf = 1;
    }

AVL樹的刪除

  • 從AVL樹中刪除一個節點,首先要判斷這個節點是否在樹中,若存在,刪除就可能導致AVL樹不滿足性質的情況,就要進行旋轉調整。
  • 被刪除節點存在一下幾種情況:
    1.刪除節點只有左孩子。
    2.刪除節點只有右孩子。
    3.刪除節點左右孩子都存在。—->變爲刪除中序遍歷下的第一個節點。
  • 更新parent的平衡因子,如果刪除節點是parent 的左孩子,那麼平衡因子+1,相反,如果是右孩子,平衡因子-1,根據修改後的parent的平衡因子修改到根節點的所有節點。
    1.如果parent的平衡因子爲1或者-1,那麼parent的高度不變,以parent爲子樹的所有節點平衡因子都沒有發生變化,不需要調整。
    2.如果parent的平衡因子變成0,雖然以parent爲根的樹平衡了,但是其高度減1了,這時候就要去檢查parent的雙親的平衡因子的情況。
    3.parent的平衡因子爲2或者-2,則較矮的子樹被縮短,需要進行平衡話旋轉。
    令parent較高子樹的根爲q,根據q的平衡因子,分一下三種情況 :
    如果q的平衡因子爲0,執行單旋轉恢復parent
    如果q的平衡因子與parent平衡因子(正負)號相同,則執行一個單旋轉恢復 parent
    如果q的平衡因子與parent平衡因子(正負)號相反,則執行一個雙旋轉恢復 parent

AVL樹的代碼:

#pragma once
#include<iostream>
#include<math.h>
using namespace std;
template<class k, class v>
struct AVLTreeNode
{
    AVLTreeNode(const k& key, const v& value)
        :_key(key)
        ,_value(value)
        ,_buf(0)
        ,_pLeft(NULL)
        ,_pRight(NULL)
        ,_pParent(NULL)
    {}

    AVLTreeNode<k, v>* _pLeft;
    AVLTreeNode<k, v>* _pRight;
    AVLTreeNode<k, v>* _pParent;
    k _key;
    v _value;
    int _buf;
};
template<class k,class v>
class AVLTree
{
    typedef AVLTreeNode<k, v> Node;
    typedef Node* pNode;
public:
    AVLTree()
        :_pRoot(NULL)
    {}
    bool Insert(const k& key, const v&value)
    {
        if (_pRoot == NULL)
        {
            _pRoot = new Node(key, value);
            return true;
        }
        pNode pCur = _pRoot;
        pNode pParent = NULL;
        while (pCur)
        {
            if (pCur->_key > key)
            {
                pParent = pCur;
                pCur = pCur->_pLeft;
            }
            else if (pCur->_key < key)
            {
                pParent = pCur;
                pCur = pCur->_pRight;
            }
            else
            {
                cout << "元素已存在,插入失敗" << endl;
                return false;
            }
        }
            pCur = new Node(key, value);
            if (pParent->_key > key)
            {
                pParent->_pLeft =pCur;
                pParent->_buf--;
            }
            else
            {
                pParent->_pRight = pCur;
                pParent->_buf++;
            }
            pCur->_pParent = pParent;
            //插入成功,接下來就是進行調整。
            //情況一,如果pParent的平衡因子是0
            while (pParent)
            {
                if (pParent->_pRight == pCur)
                    pParent->_buf++;
                else
                    pParent->_buf--;
                if (pParent->_buf == 0)
                    break;
                else if (pParent->_buf == 1 || pParent->_buf == -1)
                {
                    pCur = pParent;
                    pParent = pCur->_pParent;
                }
                else
                {
                    if (pParent->_buf == 2)
                    {
                        if (pCur->_buf == 1)
                            RotateL(pParent);
                        else if (pCur->_buf == -1)
                            RotateRL(pParent);
                    }
                    else if (pParent->_buf == -2)
                    {
                        if (pCur->_buf == -1)
                            RotateR(pParent);
                        else if (pCur->_buf == 1)
                            RotateLR(pParent);
                    }
                    break;
                }

            }

            return true;
    }
    void InOrder()
    {
        _InOrder(_pRoot);
    }

    bool IsBalance()
    {
        return _IsBalance(_pRoot);
    }

private:
    bool _IsBalance(pNode pRoot)
    {
        if (pRoot == NULL)
            return true;
        int lefttree = _IsBalance(pRoot->_pLeft);
        int righttree = _IsBalance(pRoot->_pRight);
        if (abs(righttree - lefttree) > 1)
            return false;
        return _IsBalance(pRoot->_pLeft) && _IsBalance(pRoot->_pRight);
    }
    int _Height(pNode pRoot)
    {
        if (pRoot == NULL)
            return 0;
        int left = _Height(pRoot->_pLeft);
        int right = _Height(pRoot->_pRight);
        return left > right ? left + 1 : right + 1;
    }
    void RotateL(pNode pParent)
    {
        pNode subR = pParent->_pRight;
        pNode subRL = pParent->_pRight->_pLeft;
        pParent->_pRight = subRL;
        if (subRL)
            subRL->_pParent = pParent;
        subR->_pLeft = pParent;
        pNode pparent = pParent->_pParent;
        pParent->_pParent = subR;
        subR->_pParent = pparent;
        if (pParent == _pRoot)
        {
            _pRoot = subR;
        }
        else
        {
            if (pparent->_pLeft == pParent)
                pparent->_pLeft = subR;
            else
                pparent->_pRight = subR;
        }
        pParent->_buf = subR->_buf = 0;
    }
    void RotateR(pNode pParent)
    {
        pNode subL = pParent->_pLeft;
        pNode subLR = pParent->_pLeft->_pRight;
        pParent->_pLeft = subLR;
        if (subLR)
            subLR->_pParent = pParent;
        subL->_pRight = pParent;
        pNode pparent = pParent->_pParent;
        pParent->_pParent = subL;
        subL->_pParent = pparent;
        if (pParent == _pRoot)
            _pRoot = subL;
        else
        {
            if (pparent->_pLeft == pParent)
                pparent->_pLeft = subL;
            else
                pparent->_pRight = subL;
        }
        pParent->_buf = subL->_buf = 0;
    }
    void RotateRL(pNode pParent)
    {
        pNode subR = pParent->_pRight;
        pNode subRL = NULL;
        if (subR != NULL)
            subRL = subR->_pLeft;
        RotateR(subR);
        RotateL(pParent);
        if (subRL->_buf == 1)
            pParent->_buf = -1;
        else if (subRL->_buf == -1)
            pParent->_buf = 0;
    }
    void RotateLR(pNode pParent)
    {
        pNode subL = pParent->_pLeft;
        pNode subLR = NULL;
        if (subL != NULL)
            subLR = subL->_pRight;
        RotateL(subL);
        RotateR(pParent);
        if (subLR->_buf == 1)
            pParent->_buf = 0;
        else if (subLR->_buf == -1)
            pParent->_buf = 1;
    }
    void _InOrder(pNode pRoot)
    {
        if (pRoot)
        {
            _InOrder(pRoot->_pLeft);
            cout << "<" << pRoot->_key << "," << pRoot->_value << ">" << endl;
            _InOrder(pRoot->_pRight);
        }

    }

private:
    pNode _pRoot;
};
void TestAVLTreee()
{
    int a[] = { 16,3,7,11,9,26,18,14,15 };
    AVLTree<int, int> avl;
    for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
    {
        avl.Insert(a[i], i);

    }
    avl.InOrder();
}

這裏寫圖片描述

發佈了46 篇原創文章 · 獲贊 16 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章