国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【數(shù)據(jù)結(jié)構(gòu)】AVL樹(shù)(萬(wàn)字超詳細(xì) 附動(dòng)圖)

這篇具有很好參考價(jià)值的文章主要介紹了【數(shù)據(jù)結(jié)構(gòu)】AVL樹(shù)(萬(wàn)字超詳細(xì) 附動(dòng)圖)。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

一、前言

二、AVL樹(shù)的性質(zhì)

三、AVL樹(shù)節(jié)點(diǎn)的定義

四、AVL樹(shù)的插入

五、AVL樹(shù)的平衡調(diào)整

六、AVL樹(shù)的驗(yàn)證

6.1 驗(yàn)證有序

6.2 驗(yàn)證平衡

七、AVL樹(shù)的刪除

八、AVL樹(shù)的性能和代碼


一、前言

還沒(méi)有學(xué)習(xí)過(guò)二叉搜索樹(shù)的同學(xué)可以移步

【數(shù)據(jù)結(jié)構(gòu)】二叉搜索樹(shù)-CSDN博客https://blog.csdn.net/Eristic0618/article/details/137919573?spm=1001.2014.3001.5501AVL樹(shù),又稱為平衡二叉樹(shù),它基于二叉搜索樹(shù)并通過(guò)平衡而得到。

在前面的學(xué)習(xí)中我們提到,二叉搜索樹(shù)可以提高搜索數(shù)據(jù)的效率,但在數(shù)據(jù)有序的情況下會(huì)退化為單支樹(shù),此時(shí)在樹(shù)中查找元素就得遍歷一整個(gè)分支,時(shí)間復(fù)雜度也會(huì)退化至O(N)。

【數(shù)據(jù)結(jié)構(gòu)】AVL樹(shù)(萬(wàn)字超詳細(xì) 附動(dòng)圖),數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),c++,開(kāi)發(fā)語(yǔ)言

如果有一種算法,可以使二叉搜索樹(shù)時(shí)刻保持左右子樹(shù)的平衡,就可以避免這種最壞情況。

因此,兩位俄羅斯的數(shù)學(xué)家G.M.Adelson-Velskii和E.M.Landis在1962年發(fā)明了AVL樹(shù),解決了上述問(wèn)題。


二、AVL樹(shù)的性質(zhì)

當(dāng)我們向二叉搜索樹(shù)中插入新節(jié)點(diǎn)時(shí),如果能用某種方法時(shí)刻保證樹(shù)中每個(gè)節(jié)點(diǎn)的左右子樹(shù)高度之差不超過(guò)1,就可以降低整棵樹(shù)的高度,保證每條分支的平衡

AVL樹(shù)的性質(zhì)如下:

  • AVL樹(shù)可以是空樹(shù)
  • 一顆AVL樹(shù)的左右子樹(shù)都是AVL樹(shù)
  • 一顆AVL樹(shù)的左右子樹(shù)高度差不超過(guò)1

例如:

【數(shù)據(jù)結(jié)構(gòu)】AVL樹(shù)(萬(wàn)字超詳細(xì) 附動(dòng)圖),數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),c++,開(kāi)發(fā)語(yǔ)言


三、AVL樹(shù)節(jié)點(diǎn)的定義

AVL樹(shù)的左右子樹(shù)高度差不能超過(guò)1,但是如何便捷的去檢測(cè)該性質(zhì)是否被打破呢?

我們可以在節(jié)點(diǎn)中定義一個(gè)平衡因子,如果左子樹(shù)比右子樹(shù)高一層,那么平衡因子就為-1;如果左右子樹(shù)一樣高,平衡因子就為0;如果右子樹(shù)比左子樹(shù)高一層,那么平衡因子就為1,這三種情況下AVL樹(shù)的性質(zhì)都沒(méi)有被打破。

按照這個(gè)規(guī)則,如果平衡因子為-2、2或其他值,則說(shuō)明左右子樹(shù)已經(jīng)失衡,性質(zhì)被打破。

在調(diào)整失衡的AVL樹(shù)時(shí),我們需要頻繁的訪問(wèn)父節(jié)點(diǎn),所以在AVL樹(shù)中我們需要使用三叉鏈,因此AVL樹(shù)的節(jié)點(diǎn)除了包含左右子節(jié)點(diǎn)的指針,還需要一個(gè)指向父節(jié)點(diǎn)的指針

另外需要說(shuō)明一下,本文中,我們使用key/value模型的AVL樹(shù)

AVL樹(shù)節(jié)點(diǎn)的定義如下:

template<class K,class V>
struct AVLTreeNode
{
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;
	
	pair<K, V> _kv; //第一個(gè)數(shù)據(jù)存儲(chǔ)key,第二個(gè)數(shù)據(jù)存儲(chǔ)value
	int _bf; //平衡因子(balance factor)

	AVLTreeNode(const pair<const K, V>& kv)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_kv(kv)
		,_bf(0) //新節(jié)點(diǎn)左右都為空,平衡因子為0
	{}
};

可能有些同學(xué)對(duì)pair沒(méi)有了解,這里簡(jiǎn)單介紹一下

pair可以將兩個(gè)數(shù)據(jù)組成一組元素,因此對(duì)于key/value模型這種需要用到兩個(gè)數(shù)據(jù)為一組的元素時(shí)就可以使用,內(nèi)部的成員變量為first和second,其主要使用方法為:

pair<T1, T2> p1(v1, v2); //輸入兩個(gè)數(shù)據(jù)創(chuàng)建pair類型變量
make_pair(v1, v2);       //輸入兩個(gè)數(shù)據(jù)通過(guò)函數(shù)創(chuàng)建pair類型變量
p1.first                 //訪問(wèn)p1的第一個(gè)數(shù)據(jù)
p1.second                //訪問(wèn)p1的第二個(gè)數(shù)據(jù)

四、AVL樹(shù)的插入

向AVL樹(shù)中插入節(jié)點(diǎn)與向二叉搜索樹(shù)中插入節(jié)點(diǎn)的過(guò)程基本相同,唯一的區(qū)別就是AVL樹(shù)在插入節(jié)點(diǎn)后可能存在失衡的情況,需要調(diào)整。

我們先按照二叉搜索樹(shù)的規(guī)則將節(jié)點(diǎn)插入到AVL樹(shù)中,并判斷插入的節(jié)點(diǎn)在父節(jié)點(diǎn)的左邊還是右邊

按照平衡因子的規(guī)則,如果新節(jié)點(diǎn)插入到了父節(jié)點(diǎn)的左側(cè),那么父節(jié)點(diǎn)的平衡因子-1

【數(shù)據(jù)結(jié)構(gòu)】AVL樹(shù)(萬(wàn)字超詳細(xì) 附動(dòng)圖),數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),c++,開(kāi)發(fā)語(yǔ)言

如果新節(jié)點(diǎn)插入到了父節(jié)點(diǎn)的右側(cè),那么父節(jié)點(diǎn)的平衡因子+1

【數(shù)據(jù)結(jié)構(gòu)】AVL樹(shù)(萬(wàn)字超詳細(xì) 附動(dòng)圖),數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),c++,開(kāi)發(fā)語(yǔ)言

以上,便是新增節(jié)點(diǎn)的父節(jié)點(diǎn)平衡因子可能的變化情況。

但是!插入一個(gè)節(jié)點(diǎn)不但會(huì)影響父節(jié)點(diǎn),還可能會(huì)影響到祖先節(jié)點(diǎn)

我們觀察上面的四種可能,其中左邊的兩種情況下,插入節(jié)點(diǎn)后以父節(jié)點(diǎn)為根的子樹(shù)高度發(fā)生了變化;在右邊的兩種情況下,插入節(jié)點(diǎn)后以父節(jié)點(diǎn)為根的子樹(shù)高度沒(méi)有發(fā)生變化。

觀察過(guò)后可以發(fā)現(xiàn),當(dāng)父節(jié)點(diǎn)的平衡因子從0變?yōu)?/-1后,子樹(shù)高度發(fā)生變化;當(dāng)父節(jié)點(diǎn)的平衡因子從1/-1變?yōu)?后,子樹(shù)高度不發(fā)生變化?

如果以父節(jié)點(diǎn)為根的子樹(shù)高度沒(méi)有發(fā)生變化,那么就不會(huì)影響到祖先節(jié)點(diǎn)的平衡因子;如果高度變了就會(huì)繼續(xù)向上影響到祖先節(jié)點(diǎn)的平衡因子

因此,我們可以通過(guò)判斷節(jié)點(diǎn)的插入位置來(lái)計(jì)算父節(jié)點(diǎn)的平衡因子,進(jìn)而判斷子樹(shù)高度是否發(fā)生變化,再進(jìn)一步計(jì)算對(duì)祖先節(jié)點(diǎn)平衡因子的影響,來(lái)判斷AVL樹(shù)是否失衡。

至此,我們已經(jīng)可以開(kāi)始寫插入新節(jié)點(diǎn)和更新平衡因子的代碼了:

template<class K, class V>
class AVLTree
{
	typedef AVLTreeNode<K, V> Node;
public:
	bool insert(const pair<const K, V>& kv)
	{
		if (_root == nullptr) //檢測(cè)為空樹(shù)的情況
		{
			_root = new Node(kv);
			return true;
		}

		Node* parent = nullptr;
		Node* cur = _root;

		while (cur) //搜索新節(jié)點(diǎn)的插入位置
		{
			parent = cur;
			if (kv.first > cur->_kv.first)
				cur = cur->_right;
			else if (kv.first < cur->_kv.first)
				cur = cur->_left;
			else
				return false;
		}

		cur = new Node(kv);
        //將父節(jié)點(diǎn)與新節(jié)點(diǎn)鏈接
        //比較新節(jié)點(diǎn)和父節(jié)點(diǎn)的key判斷插入到左邊還是右邊
		if (kv.first > parent->_kv.first) //這里防止有人看不懂再?gòu)?qiáng)調(diào)一遍,kv是pair類型的對(duì)象,kv.first是key,kv.second是value
		{
			parent->_right = cur;
			cur->_parent = parent;
		}
		else
		{
			parent->_left = cur;
			cur->_parent = parent;
		}

		while (cur != _root)
		{
            //插入節(jié)點(diǎn)后除了對(duì)父節(jié)點(diǎn)造成影響還可能對(duì)祖宗節(jié)點(diǎn)造成影響
            //因此隨著循環(huán)進(jìn)行,這里的cur不一定為新節(jié)點(diǎn),可以理解為高度發(fā)生變化的子樹(shù)的根節(jié)點(diǎn)
            //更新父節(jié)點(diǎn)的平衡因子
			if (cur == parent->_left)
				parent->_bf--;
			else
				parent->_bf++;

            //更新后檢測(cè)父節(jié)點(diǎn)的平衡因子
			if (parent->_bf == 0) //平衡因子為0說(shuō)明沒(méi)有打破性質(zhì),跳出循環(huán)
				break;
			else if (parent->_bf == 1 || parent->_bf == -1) //更新后平衡因子為1或-1說(shuō)明高度發(fā)生變化,改變cur和parent的指向后繼續(xù)向上更新
			{
				cur = parent;
				parent = parent->_parent;
			}
			else if (parent->_bf == 2 || parent->_bf == -2) //更新后平衡因子為2或-2.說(shuō)明已經(jīng)失衡,需要調(diào)整
			{
                //不同情況的調(diào)整方法...
				if (parent->_bf == 2)
				{
					if (cur->_bf == 1)
					{
						//...
					}
					else if (cur->_bf == -1)
					{
						//...
					}
				}
				else
				{
					if (cur->_bf == 1)
					{
						//...
					}
					else if (cur->_bf == -1)
					{
						//...
					}
				}
				break;
			}
			else //平衡因子出現(xiàn)意外情況,報(bào)錯(cuò)
			{
				assert(false);
			}
		}

		return true;
	}

private:
	Node* _root = nullptr;
};

五、AVL樹(shù)的平衡調(diào)整(附動(dòng)圖)

如果在一顆原本平衡的AVL樹(shù)中插入一個(gè)新節(jié)點(diǎn),可能會(huì)造成失衡,此時(shí)需要調(diào)整樹(shù)的結(jié)構(gòu)使之重新平衡,這種調(diào)整方法稱為旋轉(zhuǎn)。

根據(jù)樹(shù)的原本結(jié)構(gòu)和節(jié)點(diǎn)插入位置的不同分為四種情況四種旋轉(zhuǎn)方式

(1)新節(jié)點(diǎn)插入較高左子樹(shù)左側(cè):右單旋

【數(shù)據(jù)結(jié)構(gòu)】AVL樹(shù)(萬(wàn)字超詳細(xì) 附動(dòng)圖),數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),c++,開(kāi)發(fā)語(yǔ)言

自己做的動(dòng)圖大家有需要自取~這里就不加水印了

問(wèn)題來(lái)了:如何判斷插入的新節(jié)點(diǎn)的方位呢?

很簡(jiǎn)單,以上面的情況為例,插入新節(jié)點(diǎn)后60的平衡因子變成-2,說(shuō)明左子樹(shù)更高,而30的平衡因子變成-1,說(shuō)明新節(jié)點(diǎn)插入到了30的左子樹(shù)。后面左單旋以及雙旋中都同理,我們使用平衡因子就可以判斷新節(jié)點(diǎn)插入的位置

右單旋代碼如下:

void RotateRight(Node *parent) //parent為平衡因子發(fā)生失衡的節(jié)點(diǎn)
{
    Node *subL = parent->_left; //subL為parent的左子節(jié)點(diǎn)
    Node *subLR = subL->_right; //subLR為subL的右子節(jié)點(diǎn)
    //parent,subL和subLR三個(gè)節(jié)點(diǎn)是旋轉(zhuǎn)中唯三需要進(jìn)行操作的三個(gè)節(jié)點(diǎn)

    // 將parent與subLR節(jié)點(diǎn)進(jìn)行鏈接
    parent->_left = subLR;
    if (subLR) //subLR可能為空
        subLR->_parent = parent;

    Node *parentParent = parent->_parent; //記錄parent的父節(jié)點(diǎn)

    if (parent != _root)
    {
        subL->_parent = parentParent; //將subL與parent的父節(jié)點(diǎn)鏈接
        if (parent == parentParent->_left)
            parentParent->_left = subL;
        else
            parentParent->_right = subL;
    }
    else //如果parent為根,旋轉(zhuǎn)后subL成為新的根
    {
        _root = subL;
        subL->_parent = nullptr;
    }

    //將subL與parent鏈接
    subL->_right = parent;
    parent->_parent = subL;

    parent->_bf = subL->_bf = 0; //更新平衡因子
}

(2)新節(jié)點(diǎn)插入較高右子樹(shù)右側(cè):左單旋

【數(shù)據(jù)結(jié)構(gòu)】AVL樹(shù)(萬(wàn)字超詳細(xì) 附動(dòng)圖),數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),c++,開(kāi)發(fā)語(yǔ)言

因?yàn)樽髥涡脑砗陀覇涡穷愃频?,只要理解了右單旋,加上?dòng)圖的配合,左單旋和后面的雙旋都是很好理解的?

左單旋代碼如下:

void RotateLeft(Node *parent)
{
    Node *subR = parent->_right;
    Node *subRL = subR->_left;

    parent->_right = subRL;
    if (subRL)
        subRL->_parent = parent;

    Node *parentParent = parent->_parent;

    if (parent != _root)
    {
        subR->_parent = parentParent;
        if (parent == parentParent->_left)
            parentParent->_left = subR;
        else
            parentParent->_right = subR;
    }
    else
    {
        _root = subR;
        subR->_parent = nullptr;
    }

    subR->_left = parent;
    parent->_parent = subR;

    parent->_bf = subR->_bf = 0;
}

(3)新節(jié)點(diǎn)插入較高左子樹(shù)右側(cè):先左單旋再右單旋(左右雙旋)?

這種情況又可以分為兩種情況:

【數(shù)據(jù)結(jié)構(gòu)】AVL樹(shù)(萬(wàn)字超詳細(xì) 附動(dòng)圖),數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),c++,開(kāi)發(fā)語(yǔ)言

不過(guò)這兩種情況都屬于在較高左子樹(shù)的右側(cè)插入,處理方式都是相同的,唯一的區(qū)別在于最后旋轉(zhuǎn)完成后,更新平衡因子時(shí)的值不同。

接下來(lái)我們以上面的那個(gè)情況為例展示左右雙旋的過(guò)程:

【數(shù)據(jù)結(jié)構(gòu)】AVL樹(shù)(萬(wàn)字超詳細(xì) 附動(dòng)圖),數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),c++,開(kāi)發(fā)語(yǔ)言

而下面的情況和上面的情況唯一的區(qū)別在于,最后更新的平衡因子不同

【數(shù)據(jù)結(jié)構(gòu)】AVL樹(shù)(萬(wàn)字超詳細(xì) 附動(dòng)圖),數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),c++,開(kāi)發(fā)語(yǔ)言

如何去決定每個(gè)節(jié)點(diǎn)更新后的平衡因子呢?可以看到這兩種情況中,如果在b下面插入新節(jié)點(diǎn),那么旋轉(zhuǎn)過(guò)后30和60的平衡因子更新成0,90的平衡因子更新成1;如果在c下面插入新節(jié)點(diǎn),則是60和90的平衡因子更新成0,30的平衡因子更新成-1

而新節(jié)點(diǎn)究竟插入到了b下面還是在c下面,我們可以通過(guò)插入節(jié)點(diǎn)后60的平衡因子來(lái)判斷

【數(shù)據(jù)結(jié)構(gòu)】AVL樹(shù)(萬(wàn)字超詳細(xì) 附動(dòng)圖),數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),c++,開(kāi)發(fā)語(yǔ)言

左右雙旋代碼如下:

void RotateLR(Node *parent)
{
    Node *subL = parent->_left;
    Node *subLR = subL->_right;
    int bf = subLR->_bf; //記錄插入節(jié)點(diǎn)后subLR的平衡因子

    RotateLeft(subL); //先左單旋
    RotateRight(parent); //再右單旋

    //更新平衡因子
    //通過(guò)前面記錄的平衡因子判斷更新的情況
    if (bf == 0)
    {
        parent->_bf = subL->_bf = subLR->_bf = 0;
    }
    else if (bf == 1)
    {
        subL->_bf = -1;
        parent->_bf = subLR->_bf = 0;
    }
    else if (bf == -1)
    {
        parent->_bf = 1;
        subL->_bf = subLR->_bf = 0;
    }
    else
    {
        assert(false);
    }
}

(4)新節(jié)點(diǎn)插入較高右子樹(shù)左側(cè):先右單旋再左單旋(右左雙旋)

這種情況和左右雙旋的情況原理一樣,我們直接上動(dòng)圖和代碼

【數(shù)據(jù)結(jié)構(gòu)】AVL樹(shù)(萬(wàn)字超詳細(xì) 附動(dòng)圖),數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),c++,開(kāi)發(fā)語(yǔ)言

右左雙旋的代碼如下:

void RotateRL(Node *parent)
{
    Node *subR = parent->_right;
    Node *subRL = subR->_left;
    int bf = subRL->_bf;

    RotateRight(subR);
    RotateLeft(parent);

    if (bf == 0)
    {
        parent->_bf = subR->_bf = subRL->_bf = 0;
    }
    else if (bf == 1)
    {
        parent->_bf = -1;
        subR->_bf = subRL->_bf = 0;
    }
    else if (bf == -1)
    {
        subR->_bf = 1;
        parent->_bf = subRL->_bf = 0;
    }
    else
    {
        assert(false);
    }
}

現(xiàn)在四個(gè)旋轉(zhuǎn)的函數(shù)都實(shí)現(xiàn)了,完整的插入函數(shù)代碼如下:

bool insert(const pair<const K, V> &kv)
{
    if (_root == nullptr)
    {
        _root = new Node(kv);
        return true;
    }

    Node *parent = nullptr;
    Node *cur = _root;

    while (cur)
    {
        parent = cur;
        if (kv.first > cur->_kv.first)
            cur = cur->_right;
        else if (kv.first < cur->_kv.first)
            cur = cur->_left;
        else
            return false;
    }

    cur = new Node(kv);
    if (kv.first > parent->_kv.first)
    {
        parent->_right = cur;
        cur->_parent = parent;
    }
    else
    {
        parent->_left = cur;
        cur->_parent = parent;
    }

    while (cur != _root)
    {
        if (cur == parent->_left)
            parent->_bf--;
        else
            parent->_bf++;

        if (parent->_bf == 0)
            break;
        else if (parent->_bf == 1 || parent->_bf == -1)
        {
            cur = parent;
            parent = parent->_parent;
        }
        else if (parent->_bf == 2 || parent->_bf == -2)
        {
            if (parent->_bf == 2) //說(shuō)明右子樹(shù)更高
            {
                if (cur->_bf == 1) //說(shuō)明新節(jié)點(diǎn)插入到了右邊,符合在更高右子樹(shù)的右側(cè)插入的情況
                {
                    RotateLeft(parent); //執(zhí)行左單旋
                }
                else if (cur->_bf == -1) //說(shuō)明新節(jié)點(diǎn)插入到了左邊,符合在更高右子樹(shù)的左側(cè)插入的情況
                {
                    RotateRL(parent); //執(zhí)行右左雙旋
                }
            }
            else //左子樹(shù)更高
            {
                if (cur->_bf == 1) //說(shuō)明新節(jié)點(diǎn)插入到了右邊,符合在更高左子樹(shù)的右側(cè)插入的情況
                {
                    RotateLR(parent); //執(zhí)行左右雙旋
                }
                else if (cur->_bf == -1) //說(shuō)明新節(jié)點(diǎn)插入到了左邊,符合在更高左子樹(shù)的左側(cè)插入的情況
                {
                    RotateRight(parent); //執(zhí)行右單旋
                }
            }
            break;
        }
        else
        {
            assert(false);
        }
    }

    return true;
}

六、AVL樹(shù)的驗(yàn)證

6.1 驗(yàn)證有序

最重要的插入節(jié)點(diǎn)部分完成了,不過(guò)在驗(yàn)證是否符合AVL樹(shù)性質(zhì)前,我們首先需要驗(yàn)證其是否是一棵二叉搜索樹(shù)

在之前講解二叉搜索樹(shù)中提到過(guò),如果中序遍歷能夠得到一個(gè)有序的序列,就說(shuō)明是二叉搜索樹(shù)

中序遍歷代碼如下:

void InOrder()
{
    _InOrder(_root);
    cout << endl;
}

void _InOrder(Node *root)
{
    if (root == nullptr)
        return;
    _InOrder(root->_left);
    cout << root->_kv.first << " "; // key/value模型,我們只打印key即可
    _InOrder(root->_right);
}

驗(yàn)證:

【數(shù)據(jù)結(jié)構(gòu)】AVL樹(shù)(萬(wàn)字超詳細(xì) 附動(dòng)圖),數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),c++,開(kāi)發(fā)語(yǔ)言

說(shuō)明符合二叉搜索樹(shù)性質(zhì)

6.2 驗(yàn)證平衡

要驗(yàn)證是否符合AVL樹(shù)性質(zhì),只需要檢測(cè)它的所有節(jié)點(diǎn)的子樹(shù)高度差不超過(guò)1即可

需要注意的是,這里不可以直接通過(guò)判斷平衡因子的絕對(duì)值是否大于1來(lái)驗(yàn)證平衡,因?yàn)槠胶庖蜃邮遣豢陀^的,可以被修改

因此,我們通過(guò)遞歸來(lái)得到每棵子樹(shù)的高度并進(jìn)行判斷即可

代碼如下:

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

bool _IsBalance(Node *root)
{
    if (root == nullptr)
        return true;
    int leftHeigit = _Height(root->_left);
    int rightHeight = _Height(root->_right);
    if (rightHeight - leftHeigit != root->_bf)
    {
        cout << root->_kv.first << "平衡因子異常" << endl;
        return false;
    }

    return abs(rightHeight - leftHeigit) <= 1 
        && _IsBalance(root->_left) 
        && _IsBalance(root->_right);
}

int Height()
{
    return _Height(_root);
}

int _Height(Node *root)
{
    if (root == nullptr)
        return 0;
    int higher = max(_Height(root->_left), _Height(root->_right));
    return higher + 1;
}

驗(yàn)證:

【數(shù)據(jù)結(jié)構(gòu)】AVL樹(shù)(萬(wàn)字超詳細(xì) 附動(dòng)圖),數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),c++,開(kāi)發(fā)語(yǔ)言

如果在驗(yàn)證是否是AVL樹(shù)中需要更多大量的測(cè)試用例,我們可以取一些隨機(jī)數(shù):

int main()
{
	const int N = 1000;
	vector<int> v;
	v.reserve(N);
	srand(time(0));

	for (int i = 0; i < N; i++)
	{
		v.push_back(rand() + i);
	}

	AVLTree<int, int> t;
	for (auto i : v)
	{
		t.insert(make_pair(i, 1));
	}
	t.InOrder();
	if (t.IsBalance())
		cout << "是AVL樹(shù)" << endl;
	else
		cout << "不是AVL樹(shù)" << endl;
	return 0;
}

?


七、AVL樹(shù)的刪除

AVL樹(shù)的刪除并不是本文的重點(diǎn),因?yàn)槠湓砦覀冊(cè)谇懊嬉呀?jīng)學(xué)習(xí)過(guò)了

我們只需要按照二叉搜索樹(shù)的方式來(lái)對(duì)目標(biāo)節(jié)點(diǎn)進(jìn)行刪除,再判斷是否失衡,如果失衡則旋轉(zhuǎn)即可


八、AVL樹(shù)的性能和代碼

AVL樹(shù)追求的是嚴(yán)格平衡,因此可以保證查找時(shí)高效的時(shí)間復(fù)雜度O(logN),但是如果我們需要頻繁的對(duì)其進(jìn)行旋轉(zhuǎn)來(lái)維護(hù)平衡,一定程度上會(huì)影響效率,尤其是刪除節(jié)點(diǎn)時(shí)的最差情況下我們可能需要一路旋轉(zhuǎn)到根的位置。

相對(duì)于AVL樹(shù)的嚴(yán)格平衡,紅黑樹(shù)則追求一種相對(duì)平衡,因此會(huì)略勝一籌,后面的文章中會(huì)對(duì)紅黑樹(shù)進(jìn)行講解。

AVL樹(shù)的完整代碼如下:

template<class K,class V>
struct AVLTreeNode
{
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;
	
	pair<K, V> _kv;
	int _bf; //平衡因子

	AVLTreeNode(const pair<const K, V>& kv)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_kv(kv)
		,_bf(0)
	{}
};

template<class K, class V>
class AVLTree
{
	typedef AVLTreeNode<K, V> Node;
public:
	bool insert(const pair<const K, V>& kv)
	{
		if (_root == nullptr)
		{
			_root = new Node(kv);
			return true;
		}

		Node* parent = nullptr;
		Node* cur = _root;

		while (cur)
		{
			parent = cur;
			if (kv.first > cur->_kv.first)
				cur = cur->_right;
			else if (kv.first < cur->_kv.first)
				cur = cur->_left;
			else
				return false;
		}

		cur = new Node(kv);
		if (kv.first > parent->_kv.first)
		{
			parent->_right = cur;
			cur->_parent = parent;
		}
		else
		{
			parent->_left = cur;
			cur->_parent = parent;
		}

		while (cur != _root)
		{
			if (cur == parent->_left)
				parent->_bf--;
			else
				parent->_bf++;

			if (parent->_bf == 0)
				break;
			else if (parent->_bf == 1 || parent->_bf == -1)
			{
				cur = parent;
				parent = parent->_parent;
			}
			else if (parent->_bf == 2 || parent->_bf == -2)//平衡異常
			{
				if (parent->_bf == 2)
				{
					if (cur->_bf == 1)
					{
						RotateLeft(parent);
					}
					else if (cur->_bf == -1)
					{
						RotateRL(parent);
					}
				}
				else
				{
					if (cur->_bf == 1)
					{
						RotateLR(parent);
					}
					else if (cur->_bf == -1)
					{
						RotateRight(parent);
					}
				}
				break;
			}
			else
			{
				assert(false);
			}
		}

		return true;
	}

	void RotateLeft(Node* parent) //新節(jié)點(diǎn)插入較高右子樹(shù)的右側(cè):左單旋
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		parent->_right = subRL;
		if(subRL)
			subRL->_parent = parent;

		Node* parentParent = parent->_parent;

		if (parent != _root)
		{
			subR->_parent = parentParent;
			if (parent == parentParent->_left)
				parentParent->_left = subR;
			else
				parentParent->_right = subR;
		}
		else
		{
			_root = subR;
			subR->_parent = nullptr;
		}

		subR->_left = parent;
		parent->_parent = subR;

		parent->_bf = subR->_bf = 0;
	}

	void RotateRight(Node* parent) //新節(jié)點(diǎn)插入較高左子樹(shù)的左側(cè):右單旋
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		parent->_left = subLR;
		if (subLR)
			subLR->_parent = parent;

		Node* parentParent = parent->_parent;

		if (parent != _root)
		{
			subL->_parent = parentParent;
			if (parent == parentParent->_left)
				parentParent->_left = subL;
			else
				parentParent->_right = subL;
		}
		else
		{
			_root = subL;
			subL->_parent = nullptr;
		}

		subL->_right = parent;
		parent->_parent = subL;

		parent->_bf = subL->_bf = 0;
	}

	void RotateRL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		int bf = subRL->_bf;

		RotateRight(subR);
		RotateLeft(parent);

		if (bf == 0)
		{
			parent->_bf = subR->_bf = subRL->_bf = 0;
		}
		else if (bf == 1)
		{
			parent->_bf = -1;
			subR->_bf = subRL->_bf = 0;
		}
		else if (bf == -1)
		{
			subR->_bf = 1;
			parent->_bf = subRL->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}

	void RotateLR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		int bf = subLR->_bf;

		RotateLeft(subL);
		RotateRight(parent);

		if (bf == 0)
		{
			parent->_bf = subL->_bf = subLR->_bf = 0;
		}
		else if (bf == 1)
		{
			subL->_bf = -1;
			parent->_bf = subLR->_bf = 0;
		}
		else if (bf == -1)
		{
			parent->_bf = 1;
			subL->_bf = subLR->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}

	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}

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

	int Height()
	{
		return _Height(_root);
	}

	size_t Size()
	{
		return _Size(_root);
	}

	Node* Find(const K& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (key > cur->_kv.first)
				cur = cur->_right;
			else if (key < cur->_kv.first)
				cur = cur->_left;
			else
				return cur;
		}
		return nullptr;
	}
private:
	void _InOrder(Node* root)
	{
		if (root == nullptr)
			return;
		_InOrder(root->_left);
		cout << root->_kv.first << " ";
		_InOrder(root->_right);
	}

	bool _IsBalance(Node* root)
	{
		if (root == nullptr)
			return true;
		int leftHeigit = _Height(root->_left);
		int rightHeight = _Height(root->_right);
		if (rightHeight - leftHeigit != root->_bf)
		{
			cout << root->_kv.first << "平衡因子異常" << endl;
			return false;
		}

		return abs(rightHeight - leftHeigit) <= 1 
			&& _IsBalance(root->_left)
			&& _IsBalance(root->_right);
	}

	int _Height(Node* root)
	{
		if (root == nullptr)
			return 0;
		int higher = max(_Height(root->_left), _Height(root->_right));
		return higher + 1;
	}

	size_t _Size(Node* root)
	{
		if (root == nullptr)
			return 0;
		return _Size(root->_left) + _Size(root->_right) + 1;
	}
private:
	Node* _root = nullptr;
};

如果有錯(cuò)誤的地方,歡迎在評(píng)論區(qū)指出

完.文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-857789.html

到了這里,關(guān)于【數(shù)據(jù)結(jié)構(gòu)】AVL樹(shù)(萬(wàn)字超詳細(xì) 附動(dòng)圖)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 計(jì)算機(jī)網(wǎng)絡(luò)知識(shí)匯總(十萬(wàn)字超詳細(xì))

    計(jì)算機(jī)網(wǎng)絡(luò)知識(shí)匯總(十萬(wàn)字超詳細(xì))

    計(jì)算機(jī)網(wǎng)絡(luò)概念: 計(jì)算機(jī)網(wǎng)絡(luò):是一個(gè)將分散的、具有獨(dú)立功能的計(jì)算機(jī)系統(tǒng),通過(guò)通信設(shè)備與線路連接起來(lái),由功能完善的軟件實(shí)現(xiàn) 資源共享 和信息傳遞的系統(tǒng) 計(jì)算機(jī)網(wǎng)絡(luò)的功能 數(shù)據(jù)通信 資源共享 分布式處理 提高可靠性 負(fù)載均衡 計(jì)算機(jī)網(wǎng)絡(luò)的組成 組成部分:硬件、

    2024年02月05日
    瀏覽(56)
  • 數(shù)據(jù)結(jié)構(gòu)之進(jìn)階二叉樹(shù)(二叉搜索樹(shù)和AVL樹(shù)、紅黑樹(shù)的實(shí)現(xiàn))超詳細(xì)解析,附實(shí)操圖和搜索二叉樹(shù)的實(shí)現(xiàn)過(guò)程圖

    數(shù)據(jù)結(jié)構(gòu)之進(jìn)階二叉樹(shù)(二叉搜索樹(shù)和AVL樹(shù)、紅黑樹(shù)的實(shí)現(xiàn))超詳細(xì)解析,附實(shí)操圖和搜索二叉樹(shù)的實(shí)現(xiàn)過(guò)程圖

    緒論? “生命有如鐵砧,愈被敲打,愈能發(fā)出火花。——伽利略”;本章主要是數(shù)據(jù)結(jié)構(gòu) 二叉樹(shù)的進(jìn)階知識(shí),若之前沒(méi)學(xué)過(guò)二叉樹(shù)建議看看這篇文章一篇掌握二叉樹(shù),本章的知識(shí)從淺到深的 對(duì)搜索二叉樹(shù)的使用進(jìn)行了介紹和對(duì)其底層邏輯的實(shí)現(xiàn)進(jìn)行了講解 ,希望能對(duì)你有所

    2024年02月04日
    瀏覽(28)
  • 十種排序算法(附動(dòng)圖)

    十種排序算法(附動(dòng)圖)

    一、基本介紹 ? 排序算法比較基礎(chǔ),但是設(shè)計(jì)到很多計(jì)算機(jī)科學(xué)的想法,如下: ? 1、比較和非比較的策略 ? 2、迭代和遞歸的實(shí)現(xiàn) ? 3、分而治之思想 ? 4、最佳、最差、平均情況時(shí)間復(fù)雜度分析 ? 5、隨機(jī)算法 二、排序算法的分類 算法分類 算法總結(jié) 三、冒泡排序 3.

    2024年02月13日
    瀏覽(19)
  • 【數(shù)據(jù)結(jié)構(gòu)】AVL 樹(shù)

    【數(shù)據(jù)結(jié)構(gòu)】AVL 樹(shù)

    前面對(duì) map / multimap / set / multiset 進(jìn)行了簡(jiǎn)單的介紹【C++】map set,在其文檔介紹中發(fā)現(xiàn),這幾個(gè)容器有個(gè)共同點(diǎn)是: 其底層都是按照二叉搜索樹(shù)來(lái)實(shí)現(xiàn)的 ,但是二叉搜索樹(shù)有其自身的缺陷,假如往樹(shù)中插入的元素有序或者接近有序,二叉搜索樹(shù)就會(huì)退化成單支樹(shù),時(shí)間復(fù)雜度

    2024年04月12日
    瀏覽(19)
  • 【數(shù)據(jù)結(jié)構(gòu)】AVL樹(shù)

    【數(shù)據(jù)結(jié)構(gòu)】AVL樹(shù)

    ??作者:一只大喵咪1201 ??專欄:《數(shù)據(jù)結(jié)構(gòu)與算法》 ??格言: 你只管努力,剩下的交給時(shí)間! 我們知道,二叉搜索樹(shù)的搜索效率非常高,平均時(shí)間復(fù)雜度是O(log 2 N),但是當(dāng)數(shù)據(jù)原本就有序時(shí),插入二叉樹(shù)中就會(huì)形成單支結(jié)構(gòu),此時(shí)搜索的時(shí)間復(fù)雜度就是O(N)。 為了避免

    2023年04月18日
    瀏覽(24)
  • [數(shù)據(jù)結(jié)構(gòu)]-AVL樹(shù)

    [數(shù)據(jù)結(jié)構(gòu)]-AVL樹(shù)

    前言 作者:小蝸牛向前沖 名言:我可以接受失敗,但我不能接受放棄 ??如果覺(jué)的博主的文章還不錯(cuò)的話,還請(qǐng) 點(diǎn)贊,收藏,關(guān)注??支持博主。如果發(fā)現(xiàn)有問(wèn)題的地方歡迎?大家在評(píng)論區(qū)指正 目錄 一、AVL樹(shù)基本知識(shí) 1、概念 2、節(jié)點(diǎn)定義 3、插入 二、AVL樹(shù)的旋轉(zhuǎn) 1、右單旋

    2024年02月04日
    瀏覽(24)
  • C++&&數(shù)據(jù)結(jié)構(gòu)——AVL樹(shù)

    C++&&數(shù)據(jù)結(jié)構(gòu)——AVL樹(shù)

    根據(jù)前面對(duì)二叉搜索樹(shù)的學(xué)習(xí)我們可以了解到二叉搜索樹(shù)可以提高查找的效率,但是如果數(shù)據(jù)本身有序,搜索樹(shù)將退化成單支樹(shù),查找時(shí)相當(dāng)于順序表查找,效率低下,如下圖: 為了解決上面的問(wèn)題,來(lái)自俄羅斯的兩位天才數(shù)學(xué)家G.M.Adelson-Velskii和E.M.Landis在1962年發(fā)明了一種方

    2024年01月19日
    瀏覽(19)
  • 【高階數(shù)據(jù)結(jié)構(gòu)】AVL樹(shù)詳解

    【高階數(shù)據(jù)結(jié)構(gòu)】AVL樹(shù)詳解

    前面對(duì)map/multimap/set/multiset進(jìn)行了簡(jiǎn)單的介紹,在其文檔介紹中發(fā)現(xiàn)。 這幾個(gè)容器有個(gè)共同點(diǎn)是: 其底層都是按照二叉搜索樹(shù)來(lái)實(shí)現(xiàn)的,但是二叉搜索樹(shù)有其自身的缺陷,假如往樹(shù)中插入的元素有序或者接近有序,二叉搜索樹(shù)就會(huì)退化成單支樹(shù),時(shí)間復(fù)雜度會(huì)退化成O(N),因此

    2024年02月12日
    瀏覽(32)
  • 數(shù)據(jù)結(jié)構(gòu)進(jìn)階(一):AVL樹(shù)

    數(shù)據(jù)結(jié)構(gòu)進(jìn)階(一):AVL樹(shù)

    所謂的AVL樹(shù)也叫做高度平衡的二叉搜索樹(shù)。 啥是高度平衡的二叉搜索樹(shù)? 高度平衡的二叉搜索樹(shù): 意味著左右子樹(shù)的高度最大不超過(guò)一 。 我們先來(lái)回顧一下二叉搜索樹(shù)的概念: 二叉搜索樹(shù)又稱二叉排序樹(shù),它或者是一棵空樹(shù) ,或者是具有以下性質(zhì)的二叉樹(shù): 若它的左子樹(shù)

    2024年02月12日
    瀏覽(17)
  • 【數(shù)據(jù)結(jié)構(gòu)】AVL樹(shù)/紅黑樹(shù)

    【數(shù)據(jù)結(jié)構(gòu)】AVL樹(shù)/紅黑樹(shù)

    目錄 1.AVL樹(shù)(高度平衡二叉搜索樹(shù)) 10.1.基本概念 10.2.實(shí)現(xiàn) 10.2.1.AVL樹(shù)節(jié)點(diǎn)的定義 10.2.2.AVL樹(shù)的插入 10.2.3.AVL樹(shù)的旋轉(zhuǎn) 1.新節(jié)點(diǎn)插入較高左子樹(shù)的左側(cè)---左左:右單旋 2.新節(jié)點(diǎn)插入較高右子樹(shù)的右側(cè)---右右:左單旋 3.新節(jié)點(diǎn)插入較高左子樹(shù)的右側(cè)---左右:先左單旋再右單旋(左

    2024年02月15日
    瀏覽(22)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包