最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C++?AVL樹概念與實現(xiàn)詳解

 更新時間:2026年05月05日 11:04:55   作者:進擊的荊棘  
這篇文章主要介紹了C++?AVL樹概念與實現(xiàn),文章帶你深入平衡二叉樹原理,掌握旋轉(zhuǎn)平衡機制,從零實現(xiàn)?AVL?樹,理解?C++?高效數(shù)據(jù)結(jié)構(gòu)的設(shè)計與核心思想,需要的朋友可以參考下

1.AVL的概念

●AVL樹是最先發(fā)明的自平衡二叉查找樹,AVL是一顆空樹,或具備下列性質(zhì)的二叉搜索樹:它的左右子樹都是AVL樹,且左右子樹的高度差的絕對值不超過1。AVL樹是一顆高度平衡搜索二叉樹,通過控制高度差去控制平衡。

●AVL樹得名于它的發(fā)明者G.M.Adelson-Velsky和E.M.Landis,他們在1962年的論文《An algorithm for the organization of information》中發(fā)表了它。

●AVL樹實現(xiàn)這里我們引入一個平衡因子(balance facor)的概念,每個節(jié)點都有一個平衡因子,任何節(jié)點的平衡因子等于右子樹的高度減去左子樹的高度,也就是說任何節(jié)點的平衡因子等于0/1/-1,AVL樹并不是必須要平衡因子,但是有了平衡因子可以更方便我們?nèi)ミM行觀察和控制樹是否平衡,就像一個風(fēng)向標(biāo)一樣。

●為什么AVL樹是高度平衡搜索二叉樹,要求高度差不超過1,而不是高度差是0呢?0不是更好的平衡嗎?通過畫圖我們可以發(fā)現(xiàn),不是不想這樣設(shè)計,而是有些情況無法做到高度差為0。如:一棵樹是2個節(jié)點,4個節(jié)點等情況下,高度差最好就是1,無法做到高度差是0.

●AVL樹整體節(jié)點數(shù)量和分布和完全二叉樹類似,高度可以控制在logN,那么增刪查改的效率也可以控制在O(logN),相比二叉搜索樹有了本質(zhì)的提升。

2.AVL樹的實現(xiàn)

2.1AVL樹的結(jié)構(gòu)

template<class k,class v>
struct AVLTreeNode{
    //需要parent指針,后續(xù)更新平衡因子需要
    pair<k,v> _kv;
    AVLTreeNode<k,v>* _left;
    AVLTreeNode<k,v>* _right;
    AVLTreeNode<k,v>* _parent;
    int _bf;//平衡因子
    AVLTreeNode(const pair<k,v>& kv)
        :_kv(kv)
        ,_left(nullptr)
        ,_right(nullptr)
        ,_parent(nullptr)
        ,_bf(0)
    {}
};
template<class k,class v>
class AVLTree{
    typedef AVLTreeNode<k,v> Node;
public:
private:
    Node* _root=nullptr;
};

2.2AVL樹的插入

2.2.1AVL樹插入一個值的大概過程

1.插入一個值按二叉搜索樹規(guī)則進行插入。

2.新增節(jié)點后,只會影響祖先節(jié)點的高度,也就是可能會影響部分祖先節(jié)點的平衡因子,所以更新從新增節(jié)點->根節(jié)點路徑上的平衡因子,實際中最壞情況下要更新到根,有些情況更新到中間就可以停止了。

3.更新平衡因子過程中沒有出現(xiàn)問題,則插入結(jié)束。

4.更新平衡因子過程中出現(xiàn)不平衡,對不平衡子樹旋轉(zhuǎn),旋轉(zhuǎn)后本質(zhì)調(diào)平衡的同時,本質(zhì)降低了子樹的高度,不會再影響上一層,所以插入結(jié)束。

2.2.2平衡因子更新

更新原則:

●平衡因子=右子樹高度-左子樹高度

●只有子樹高度變化才會影響當(dāng)前節(jié)點平衡因子

●插入節(jié)點,會增加高度,所以新增節(jié)點再parent的右子樹,parent的平衡因子++,新增節(jié)點在parent的左子樹,parent平衡因子--

●parent所在子樹的高度是否變化決定了是否會繼續(xù)往上更新

更新停止條件:

●更新后parent的平衡因子等于0,更新中parent的平衡因子變化為-1->0,說明更新前parent子樹一邊高一邊低,新增的節(jié)點插入在低的那邊,插入后parent所在的子樹高度不變,不會影響parent的父親節(jié)點的平衡因子,更新結(jié)束。

●更新后parent的平衡因子等于1或-1,更新前更新中parent的平衡因子變化為0->1或0->-1,說明更新前parent子樹兩邊一樣高,新增的插入節(jié)點后,parent所在的子樹一邊高一邊低,parent所在的子樹符合平衡要求,但是高度增加了1,會影響parent的父親節(jié)點的平衡因子,所以要繼續(xù)向上更新。

●更新后parent的平衡因子等于2或-2,更新前更新中parent的平衡因子變化為1->2或-1->-2,說明更新前parent子樹一邊高一邊低,新增的插入節(jié)點在高的那邊,parent所在的子樹高的那邊更高了,破壞了平衡,parent所在的子樹不符合平衡要求,需要旋轉(zhuǎn)處理,旋轉(zhuǎn)的目標(biāo)有兩個:1、把parent子樹旋轉(zhuǎn)平衡。2、降低parent子樹的高度,恢復(fù)到插入節(jié)點以前的高度。所以旋轉(zhuǎn)后也不需要繼續(xù)向上更新,插入結(jié)束。

●不斷更新,更新到根,根的平衡因子是1或-1也停止了。

更新到10節(jié)點,平衡因子為2,10所在的子樹已經(jīng)不平衡,需要旋轉(zhuǎn)處理

 更新到中間節(jié)點,3為根的子樹高度不變,不會影響上一層,更新結(jié)束

最壞更新到根停止

2.2.3插入節(jié)點更新平衡因子的代碼實現(xiàn)

bool Insert(const pair<k,v>& kv){
        if(!_root){
            _root=new Node(kv);
            return true;
        }
        Node* parent=nullptr;
        Node* cur=_root;
        while(cur){
            if(cur->_kv.first<kv.first){
                parent=cur;
                cur=cur->_right;
            }
            else if(cur->_kv.first>kv.first){
                parent=cur;
                cur=cur->_left;
            }
            else return false;
        }
        //開始插入
        cur=new Node(kv);
        if(cur->_kv.first<parent->_kv.first)
            parent->_left=cur;
        else parent->_right=cur;
        //父指針指向父節(jié)點
        cur->_parent=parent;
        //控制平衡
        while(parent){
            //當(dāng)節(jié)點插入左邊時,父節(jié)點平衡因子--
            if(cur==parent->_left)
                parent->_bf--;
            //當(dāng)節(jié)點插入右邊時,父節(jié)點平衡因子++
            else parent->_bf++;
            //查看樹是否依舊平衡
            if(parent->_bf==0){
                //說明父節(jié)點之前是1或-1,插入新節(jié)點后,樹可能不平衡
                break;
            }
            else if(parent->_bf==1||parent->_bf==-1){
                cur=parent;
                parent=cur->_parent;
            }
            else if(parent->_bf==2||parent->_bf==-2){
                //不平衡,旋轉(zhuǎn)
                break;
            }
            //防止樹一開始就不平衡
            else assert(false);
        }
        return true;
    }

2.3旋轉(zhuǎn)

2.3.1旋轉(zhuǎn)的原則

1.保持搜索樹的規(guī)則

2.讓旋轉(zhuǎn)的樹從不滿足并平衡,其次降低旋轉(zhuǎn)樹的高度

旋轉(zhuǎn)總共分為四種,左單選/右單旋/左右雙旋/右左雙旋。

2.3.2右單旋

●圖1展示的是10為根的樹,有a/b/c抽象為三顆高度為h的子樹(h>=0),a/b/c均符合AVL樹的要求。10可能是整棵樹的根,也可能是一整棵樹中局部的子樹的根。這里a/b/c是高度為h的子樹,是一種概括抽象表示,它代表了所有右單旋的場景,實際右單旋形態(tài)有很多種,圖2/圖3/圖4/圖5進行詳細描述。

●在a子樹中插入一個新節(jié)點,導(dǎo)致a子樹的高度從h變成h+1,不斷向上更新平衡因子,導(dǎo)致10的平衡因子從-1變成-2,10為根的樹左右高度差超過1,違反平衡規(guī)則。10為根的樹左邊太高了,需要往右邊旋轉(zhuǎn),控制兩棵樹的平衡。

●旋轉(zhuǎn)核心步驟,因為5<b子樹的值<10,將b變成10的左子樹,10變成5的右子樹,5變成這棵樹新的根,符合搜索樹的規(guī)則,控制了平衡,同時這棵樹的高度恢復(fù)到了插入之前的h+2,符合旋轉(zhuǎn)原則。若插入之前10整棵樹的局部子樹,旋轉(zhuǎn)后不會再影響上一層,插入結(jié)束。

2.3.3右單旋代碼實現(xiàn)

void RotateR(Node* parent){
        Node* subL=parent->_left;
        Node* subLR=subL->_right;
        parent->_left=subLR;
        //鏈接父節(jié)點
        if(subLR)
            subLR->_parent=parent;
        //防止找不到父結(jié)點的父結(jié)點
        Node* pparent=parent->_parent;
        subL->_right=parent;
        parent->_parent=subL;
        if(parent==_root){
            _root=subL;
            subL->_parent=nullptr;
        }
        else{
            if(pparent->_left==parent)
                pparent->_left=subL;
            else pparent->_right=subL;
            subL->_parent=pparent;
        }
        //更新平衡因子
        parent->_bf=0;
        subL->_bf=0;
    }

2.3.4左單旋

●圖6展示的是10為根的樹,有a/b/c抽象為三棵高度為h的子樹(h>=0),a/b/c均符合AVL樹的要求。10可能是整棵樹的根,也可能是一整棵樹中局部的子樹的根。這里a/b/c是高度為h的子樹,是一種概括抽象表示,它代表了所有右單旋的場景,實際右單旋形態(tài)有很多種,具體跟上面左旋類似。

●在a子樹中插入一個新節(jié)點,導(dǎo)致a子樹的高度從h變成h+1,不斷向上跟新平衡因子,導(dǎo)致10的平衡因子從1變成2,10為跟的樹左右高度差超過1,違反平衡規(guī)則。10為跟的樹右邊太高了,需要往左邊旋轉(zhuǎn),控制兩棵樹的平衡。

●旋轉(zhuǎn)核心步驟,因為10<b子樹的值<15,將b變成10的右子樹,10變成15的左子樹,15變成這棵樹新的根,符合搜索樹的規(guī)則,控制了平衡,同時這顆的高度恢復(fù)到了插入之前的h+2,符合旋轉(zhuǎn)原則。若插入之前10整棵樹的一個局部子樹,旋轉(zhuǎn)后不會再影響上一層,插入結(jié)束。

2.3.5左單旋的實現(xiàn)

void RotateL(Node* parent){
        Node* subR=parent->_right;
        Node* subRL=subR->_left;
        parent->_right=subRL;
        //鏈接父節(jié)點
        if(subRL)
            subRL->_parent=parent;
        //防止找不到父結(jié)點的父結(jié)點
        Node* pparent=parent->_parent;
        subR->_left=parent;
        parent->_parent=subR;
        if(pparent==nullptr){
            _root=subR;
            subR->_parent=nullptr;
        }
        else{
            if(parent==pparent->_left)
                pparent->_left=subR;
            else pparent->_right=subR;
            subR->_parent=pparent;
        }
        //更新平衡因子
        parent->_bf=subR->_bf=0;
    }

2.3.6左右雙旋

通過圖7和圖8可以看到,左邊高時,若插入位置不是在a子樹,而是插入在b子樹,b子樹高度從h變成h+1,引發(fā)旋轉(zhuǎn),右單旋無法解決問題,右單旋后,我們的樹依舊不平衡。右單旋解決的是存粹的左邊高,需要用兩次旋轉(zhuǎn)才能解決,以5為旋轉(zhuǎn)點進行一個左單旋,以10為旋轉(zhuǎn)點進行一個右單旋,這棵樹就平衡了。

●圖7和圖8分別為左右雙旋中h==0和h==1具體場景分析,下面將a/b/c子樹抽象為高度h的AVL子樹進行分析,另外把b子樹的細節(jié)進一步展開為8和左子樹高度為h-1的e和f子樹,因為我們要對b的父親5為旋轉(zhuǎn)點進行左單旋,左單旋需要動b樹中的左子樹。b子樹中新增節(jié)點的位置不同,平衡因子更新的細節(jié)也不同,通過觀察8的平衡因子不同,這里可以分3個場景討論。

●場景1:h>=1時,新增節(jié)點插入在e子樹,e子樹高度從h-1并為h并不斷更新8->5->10平衡因子,引發(fā)旋轉(zhuǎn),其中8的平衡因子為-1,旋轉(zhuǎn)后8和5平衡因子為0,10平衡因子為1。

●場景2:h>=1時,新增節(jié)點插入在f子樹,f子樹高度從h-1變?yōu)閔并不斷更新8->5->10平衡因子,引發(fā)旋轉(zhuǎn),其中8的平衡因子為1,旋轉(zhuǎn)后8和10平衡因子為0,5平衡因子為-1.

●場景3:h==0,a/b/c都是空樹,b自己就是一個新增節(jié)點,不斷更新5->10平衡因子,引發(fā)旋轉(zhuǎn),其中8的平衡因子為0,旋轉(zhuǎn)后8和10和5平衡因子均為0。

2.3.7左右雙旋代碼實現(xiàn)

oid RotateLR(Node* parent){
        Node* subL=parent->_left;
        Node* subLR=subL->_right;
        int bf=subLR->_bf;
        RotateL(parent->_left);
        RotateR(parent);
        if(bf==0){//更新平衡因子
            subL->_bf=0;
            subLR->_bf=0;
            parent->_bf=0;
        }
        else if(bf==-1){
            subL->_bf=0;
            subLR->_bf=0;
            parent->_bf=1;
        }
        else if(bf==1){
            subL->_bf=-1;
            subLR->_bf=0;
            parent->_bf=0;
        }
        else {
            assert(false);
        }
    }

2.3.8右左雙旋

●跟左右雙旋類似,下面將a/b/c子樹抽象為高度h的AVL子樹進行分析,另外需要把b子樹的細節(jié)進一步展開為12和左子樹高度為h-1的e和f子樹,因為我們要對b的父親15為旋轉(zhuǎn)點進行右單旋,右單旋需要動b樹中的右子樹。b子樹中新增節(jié)點的位置不同,平衡因子更新的細節(jié)也不同,通過觀察12的平衡因子不同,這里可以分三個場景討論。

●場景1:h>=1時,新增節(jié)點插入在e子樹,e子樹高度從h-1變?yōu)閔并不斷更新12->15->10平衡因子,引發(fā)旋轉(zhuǎn),其中12的平衡因子為-1,旋轉(zhuǎn)后10和12平衡因子為0,15平衡因子為1.

●場景2:h>=1時,新增節(jié)點插入在f子樹,f子樹高度從h-1變?yōu)閔并不斷更新12->15->10平衡因子,引發(fā)旋轉(zhuǎn),其中12的平衡因子為1,旋轉(zhuǎn)后15和12平衡因子為0,10平衡因子為-1。

●場景3:h==0時,a/b/c都是空樹,b自己就是一個新增節(jié)點,不斷更新15->10平衡因子,引發(fā)旋轉(zhuǎn),其中12的平衡因子為0,旋轉(zhuǎn)后10和12和15平衡因子均為0.

2.3.9右左雙旋代碼實現(xiàn)

void RotateRL(Node* parent){
        Node* subR=parent->_right;
        Node* subRL=subR->_left;
        int bf=subRL->_bf;
        RotateR(parent->_right);
        RotateL(parent);
        if(bf==0){//更新平衡因子
            subR->_bf=0;
            subRL->_bf=0;
            parent->_bf=0;
        }
        else if(bf==-1){
            subR->_bf=1;
            subRL->_bf=0;
            parent->_bf=0;
        }
        else if(bf==1){
            subR->_bf=0;
            subRL->_bf=0;
            parent->_bf=-1;
        }
        else {
            assert(false);
        }
    }

2.4AVL樹的查找

拿二叉搜索樹的邏輯就可以實現(xiàn),搜索效率為O(logN)

Node* Find(const k& key){
        Node* cur=_root;
        while(cur){
            if(cur->_kv.first<key){
                cur=cur->_right;
            }
            else if(cur->_kv.first>key){
                cur=cur->_left;
            }
            else return cur;
        }
        return nullptr;
    }

2.5AVL樹平衡檢查

實現(xiàn)的AVL樹是否合格,可以通過檢查左右子樹高度差的程度進行反向驗證,同時檢查節(jié)點的平衡因子更新是否出現(xiàn)問題。

int _Height(Node* root){
		if (root == nullptr)
			return 0;
		int leftHeight = _Height(root->_left);
		int rightHeight = _Height(root->_right);
		return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
	}
bool _IsBalanceTree(Node* root){
		// 空樹也是AVL樹
		if (nullptr == root)
			return true;
		// 計算pRoot結(jié)點的平衡因子:即pRoot左右子樹的高度差
		int leftHeight = _Height(root->_left);
		int rightHeight = _Height(root->_right);
		int diff = rightHeight - leftHeight;
		// 如果計算出的平衡因子與pRoot的平衡因子不相等,或者
		// pRoot平衡因子的絕對值超過1,則一定不是AVL樹
		if (abs(diff) >= 2)
		{
			cout << root->_kv.first << "高度差異常" << endl;
			return false;
		}
		if (root->_bf != diff)
		{
			cout << root->_kv.first << "平衡因子異常" << endl;
			return false;
		}
		// pRoot的左和右如果都是AVL樹,則該樹一定是AVL樹
		return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right);
	}
void TestAVLTree1(){
	AVLTree<int, int> t;
	// 常規(guī)的測試用例
	int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
	// 特殊的帶有雙旋場景的測試用例
	//int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
	for (auto e : a)
	{
		t.Insert({ e, e });
	}
	t.InOrder();
	cout << t.IsBalanceTree() << endl;
}
void TestAVLTree2(){
	const int N = 1000000;
	vector<int> v;
	v.reserve(N);
	srand(time(0));
	for (size_t i = 0; i < N; i++)
	{
		v.push_back(rand() + i);
	}
	size_t begin2 = clock();
	AVLTree<int, int> t;
	for (auto e : v)
	{
		t.Insert(make_pair(e, e));
	}
	size_t end2 = clock();
	cout << "Insert:" << end2 - begin2 << endl;
	cout << t.IsBalanceTree() << endl;
	cout << "Height:" << t.Height() << endl;
	cout << "Size:" << t.Size() << endl;
	size_t begin1 = clock();
	// 確定在的值
	for (auto e : v)
	{
		t.Find(e);
	}
	// 隨機值
	/*for (size_t i = 0; i < N; i++)
	{
		t.Find((rand() + i));
	}*/
	size_t end1 = clock();
	cout << "Find:" << end1 - begin1 << endl;
}

以上就是C++ AVL樹概念與實現(xiàn)詳解的詳細內(nèi)容,更多關(guān)于C++ AVL樹的實現(xiàn)的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 詳解C語言如何執(zhí)行HTTP GET請求

    詳解C語言如何執(zhí)行HTTP GET請求

    在現(xiàn)代互聯(lián)網(wǎng)時代,網(wǎng)絡(luò)數(shù)據(jù)的獲取和分析變得越來越重要,本文我們將使用C語言和libcurl庫來編寫一個簡單的網(wǎng)絡(luò)爬蟲,以執(zhí)行HTTP GET請求并獲取淘寶網(wǎng)頁的內(nèi)容,感興趣的可以了解下
    2023-11-11
  • C++ 二叉樹的鏡像實例詳解

    C++ 二叉樹的鏡像實例詳解

    這篇文章主要介紹了C++ 二叉樹的鏡像實例詳解的相關(guān)資料,需要的朋友可以參考下
    2017-06-06
  • 詳談C++的內(nèi)存泄漏問題

    詳談C++的內(nèi)存泄漏問題

    下面小編就為大家?guī)硪黄斦凜++的內(nèi)存泄漏問題。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-05-05
  • C++設(shè)計模式之訪問者模式

    C++設(shè)計模式之訪問者模式

    這篇文章主要介紹了C++設(shè)計模式之訪問者模式,本文講解了什么是訪問者模式、訪問者模式的UML類圖、訪問者模式的實現(xiàn)代碼等內(nèi)容,需要的朋友可以參考下
    2014-10-10
  • C++實現(xiàn)猜數(shù)游戲

    C++實現(xiàn)猜數(shù)游戲

    這篇文章主要為大家詳細介紹了C++實現(xiàn)猜數(shù)游戲,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-05-05
  • 判斷指定的進程或程序是否存在方法小結(jié)(vc等)

    判斷指定的進程或程序是否存在方法小結(jié)(vc等)

    VC判斷進程是否存在?比如我想知道記事本是否運行,要用到哪些函數(shù)等實例,需要的朋友可以參考下
    2013-01-01
  • c語言中 基于隨機函數(shù)的使用詳解

    c語言中 基于隨機函數(shù)的使用詳解

    本篇文章對c語言的隨機函數(shù)進行了詳細的分析介紹。需要的朋友參考下
    2013-05-05
  • C語言內(nèi)存管理及初始化細節(jié)示例詳解

    C語言內(nèi)存管理及初始化細節(jié)示例詳解

    這篇文章主要為大家介紹了C語言內(nèi)存管理及初始化細節(jié)示例的詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步
    2022-02-02
  • C++ 數(shù)據(jù)結(jié)構(gòu)之對稱矩陣及稀疏矩陣的壓縮存儲

    C++ 數(shù)據(jù)結(jié)構(gòu)之對稱矩陣及稀疏矩陣的壓縮存儲

    這篇文章主要介紹了C++ 數(shù)據(jù)結(jié)構(gòu)之對稱矩陣及稀疏矩陣的壓縮存儲的相關(guān)資料,這里實現(xiàn)稀疏矩陣和對稱矩陣的壓縮存儲的實例,需要的朋友可以參考下
    2017-08-08
  • C++實現(xiàn)神經(jīng)BP神經(jīng)網(wǎng)絡(luò)

    C++實現(xiàn)神經(jīng)BP神經(jīng)網(wǎng)絡(luò)

    這篇文章主要為大家詳細介紹了C++實現(xiàn)神經(jīng)BP神經(jīng)網(wǎng)絡(luò),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-05-05

最新評論

古浪县| 道真| 宁远县| 神木县| 灵川县| 江都市| 宜君县| 淮南市| 夏邑县| 栾城县| 乐平市| 米林县| 固安县| 松原市| 巢湖市| 金堂县| 读书| 奉新县| 班玛县| 湘潭市| 青川县| 龙门县| 陇西县| 五原县| 溧水县| 浦东新区| 安乡县| 宣汉县| 若尔盖县| 高州市| 京山县| 栖霞市| 招远市| 新余市| 道孚县| 贡觉县| 东宁县| 休宁县| 富阳市| 凤阳县| 白玉县|