【C++筆記】AVL樹的深度剖析

【C++筆記】AVL樹的深度剖析

、

🔥個人主頁大白的編程日記

🔥專欄C++筆記


文章目錄

  • 【C++筆記】AVL樹的深度剖析
    • 前言
    • 一. AVL樹的概念
    • 二.AVL樹的實現
      • 2.1 AVL樹的結構
      • 2.2 AVL樹的插入
      • 2.3 平衡因子更新
    • 三.旋轉
      • 3.1旋轉的原則
      • 3.2右單旋
      • 3.3左單旋
      • 3.4左右雙旋
      • 3.5右左雙旋
      • 3.6AVL樹的插入
      • 3.7AVL樹的查找
    • 四.AVL樹的檢測
      • 4.1AVL樹檢測
      • 4.2 AVL樹的驗證
    • 后言

前言

哈嘍,各位小伙伴大家好!上期我們講了map和set的深度剖析。今天我們來講一下AVL樹的深度剖析。話不多說,我們進入正題!向大廠沖鋒
在這里插入圖片描述

一. AVL樹的概念

  • 定義
    AVL樹是最先發明的自平衡?叉查找樹,AVL是一顆空樹,或者具備下列性質的?叉搜索樹:它的左右子樹都是AVL樹,且左右子樹的高度差的絕對值不超過1。AVL樹是?顆高度平衡搜索?叉樹,通過控制高度差去控制平衡。
    注意是所有的子樹都滿足高度差絕對值不超過1。
  • 發明者
    AVL樹得名于它的發明者G. M. Adelson-Velsky和E. M. Landis是兩個前蘇聯的科學家,他們在1962年的論文《An algorithm for the organization of information》中發表了它。
  • 平衡因子
    AVL樹實現這里我們引入?個平衡因子(balance factor)的概念,每個結點都有?個平衡因子,任何結點的平衡因子等于右子樹的高度減去左子樹的高度,也就是說任何結點的平衡因子等于0/1/-1,AVL樹并不是必須要平衡因子,但是有了平衡因子可以更方便我們去進行觀察和控制樹是否平衡,就像?個風向標?樣。

  • 高度差
    思考?下為什么AVL樹是高度平衡搜索?叉樹,要求?度差不超過1,而不是高度差是0呢?0不是更好的平衡嗎?畫畫圖分析我們發現,不是不想這樣設計,而是有些情況是做不到高度差是0的。比如?棵樹是2個結點,4個結點等情況下,高度差最好就是1,無法做到高度差是0
  • 對比二叉搜索樹
    AVL樹整體結點數量和分布和完全?叉樹類似,高度可以控制在 ,那么增刪查改的效率也可以控制在logn ,相比?叉搜索樹有了本質的提升。

二.AVL樹的實現

2.1 AVL樹的結構

這里我們為了旋轉時需要用到父親節點,所以我們就是一個三叉鏈的結構。
同時我們引入了平衡因子,方便我們控制高度差。

template<class k, class v>
struct AVLNode
{using node = AVLNode<k, v>;k _key;v _value;node* left;//左節點node* right;//右節點node* parent;//父親節點int bf;//平衡因子AVLNode(const k& key, const v& value):_key(key), _value(value), left(nullptr), right(nullptr),parent(nullptr),bf(0){}
};
template<class k, class v>
class AVLTree
{using node = AVLNode<k, v>;private:node* _root = nullptr;
};

2.2 AVL樹的插入

二叉搜索樹還是按照二叉搜索樹的規則插入。
但是插入后要更新平衡因子,如果高度差超過1那么就旋轉。

  • 插入
    插入一個值按二叉搜索樹規則進行插入。
    插入的節點,左右為空所以平衡因子為0.

  • 更新平衡因子
    新增結點以后,只會影響祖先結點的高度,也就是可能會影響部分祖先結點的平衡因子,所以更新從新增結點->根結點路徑上的平衡因子,實際中最壞情況下要更新到根,有些情況更新到中間就可以停止了,具體情況我們下面再詳細分析。

  • 結束
    更新平衡因子過程中沒有出現問題,則插入結束。

  • 旋轉
    更新平衡因子過程中出現不平衡,對不平衡子樹旋轉,旋轉后本質調平衡的同時,本質降低了子樹的高度,保持原來的高度,不會再影響上一層,所以插入結束。

2.3 平衡因子更新

更新原則:

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

  • 只有子樹高度變化才會影響當前結點平衡因子。

  • 插入結點,會增加高度,所以新增結點在parent的右子樹,parent的平衡因子++,新增結點在parent的左子樹,parent平衡因子–。

  • parent所在子樹的高度是否變化決定了是否會繼續往上更新
    如果當前子樹高度沒有變化,那當前子樹往上的祖先節點的平衡因子也不會變化
    更新結束。

更新停止條件:

  • 更新后parent的平衡因子等于0,更新中parent的平衡因子變化為-1->0 或者 1->0,說明更新前parent子樹?邊高?邊低,新增的結點插入在低的那邊,插入后parent所在的子樹高度不變,不會影響parent的?親結點的平衡因子,更新結束。

  • 更新后parent的平衡因子等于1 或 -1,更新前更新中parent的平衡因子變化為0->1 或者 0->-1,說明更新前parent子樹兩邊?樣高,新增的插入結點后,parent所在的子樹?邊高?邊低,parent所在的子樹符合平衡要求,但是高度增加了1,會影響parent的?親結點的平衡因子,所以要繼續向上更新。

  • 更新后parent的平衡因子等于2 或 -2,更新前更新中parent的平衡因子變化為1->2 或者 -1->-2,說明更新前parent子樹?邊高?邊低,新增的插入結點在高的那邊,parent所在的子樹高的那邊更高了,破壞了平衡,parent所在的子樹不符合平衡要求,需要旋轉處理,旋轉的目標有兩個:1、把parent子樹旋轉平衡。2、降低parent子樹的高度,恢復到插入結點以前的高度。所以旋轉后也不需要繼續往上更新,插入結束。

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

三.旋轉

3.1旋轉的原則

旋轉的原則如下:

  1. 保持搜索樹的規則
  2. 讓旋轉的樹從不滿足變平衡,其次降低旋轉樹的高度
    旋轉總共分為四種,左單旋/右單旋/左右雙旋/右左雙旋。
    說明:下面的圖中,有些結點我們給的是具體值,如10和5等結點,這里是為了方便講解,實際中是什么值都可以,只要大小關系符合搜索樹的性質即可。

在這里插入圖片描述

3.2右單旋

旋轉時我們需要注意保持二叉搜索樹的性質。
同時注意父親節點和平衡因子的更新。
注意旋轉后子樹的高度不變,平衡因子向上更新停止。
在這里插入圖片描述

	void RoRateR( node* parent)//右單旋{node* subL = parent->left;node* subLR = subL->right;node* pparnet = parent->parent;parent->left = subLR;if (subLR){subLR->parent = parent;//修改父節點}subL->right = parent;parent->parent = subL;if (pparnet == nullptr)//parent就是根節點{_root = subL;subL->parent = nullptr;}else{if (pparnet->left == parent)//確定parent節點是左還是右{pparnet->left = subL;}else {pparnet->right = subL;}subL->parent = pparnet;//修改父節點}subL->bf = parent->bf = 0;//更新平衡因子}

3.3左單旋

左單旋和右單旋類似。

void RoRateL( node* parent)//左單旋
{node* subR = parent->right;node* subRL = subR->left;node* pparnet = parent->parent;parent->right = subRL;if (subRL)//防止subRL為空{subRL->parent = parent;//修改父節點}subR->left = parent;parent->parent = subR;if (pparnet==nullptr)//parent就是根節點{_root = subR;subR->parent = nullptr;}else{if (pparnet->left == parent)//確定parent節點是左還是右{pparnet->left = subR;}else {pparnet->right = subR;}subR->parent = pparnet;//修改父節點}subR->bf = parent->bf = 0;//更新平衡因子
}

3.4左右雙旋

左右雙旋的就是先左單旋再右單旋。
同時注意平衡因子的更新即可。


更新條件:parent->bf == -2 && cur->bf == 1

void RoRateLR( node* parent)//左右雙旋
{node* subL = parent->left;node* subLR = subL->right;int bf = subLR->bf;//先記錄插入后的平衡因子RoRateL(subL);RoRateR(parent);if (bf == 0)//分情況討論{parent->bf = 0;subL->bf = 0;subLR->bf = 0;}else if (bf == 1){parent->bf = 0;subL->bf = -1;subLR->bf = 0;}else if (bf == -1){parent->bf = 1;subL->bf = 0;subLR->bf = 0;}else{assert(false);}
}

3.5右左雙旋

右左雙旋情況和左右雙旋類似,這里就不過多贅述了。

更新條件:parent->bf == 2 && cur->bf == -1

3.6AVL樹的插入

結合前面的知識我們就可以寫出二叉搜索樹的插入了。

void RoRateR( node* parent)//右單旋
{node* subL = parent->left;node* subLR = subL->right;node* pparnet = parent->parent;parent->left = subLR;if (subLR){subLR->parent = parent;//修改父節點}subL->right = parent;parent->parent = subL;if (pparnet == nullptr)//parent就是根節點{_root = subL;subL->parent = nullptr;}else{if (pparnet->left == parent)//確定parent節點是左還是右{pparnet->left = subL;}else {pparnet->right = subL;}subL->parent = pparnet;//修改父節點}subL->bf = parent->bf = 0;//更新平衡因子
}
void RoRateL( node* parent)//左單旋
{node* subR = parent->right;node* subRL = subR->left;node* pparnet = parent->parent;parent->right = subRL;if (subRL)//防止subRL為空{subRL->parent = parent;//修改父節點}subR->left = parent;parent->parent = subR;if (pparnet==nullptr)//parent就是根節點{_root = subR;subR->parent = nullptr;}else{if (pparnet->left == parent)//確定parent節點是左還是右{pparnet->left = subR;}else {pparnet->right = subR;}subR->parent = pparnet;//修改父節點}subR->bf = parent->bf = 0;//更新平衡因子
}
void RoRateLR( node* parent)//左右雙旋
{node* subL = parent->left;node* subLR = subL->right;int bf = subLR->bf;//先記錄插入后的平衡因子RoRateL(subL);RoRateR(parent);if (bf == 0)//分情況討論{parent->bf = 0;subL->bf = 0;subLR->bf = 0;}else if (bf == 1){parent->bf = 0;subL->bf = -1;subLR->bf = 0;}else if (bf == -1){parent->bf = 1;subL->bf = 0;subLR->bf = 0;}else{assert(false);}
}
void RoRateRL( node* parent)//右左雙旋
{node* subR = parent->right;node* subRL = subR->left;int bf = subRL->bf;//先記錄插入后的平衡因子RoRateR(subR);RoRateL(parent);if (bf == 0)//分情況討論{parent->bf = 0;subR->bf = 0;subRL->bf = 0;}else if (bf == 1){parent->bf = -1;subR->bf = 0;subRL->bf = 0;}else if (bf == -1){parent->bf = 0;subR->bf = 1;subRL->bf = 0;}else{assert(false);}
}
bool Insert(const k& x, const v& v)
{if (_root == nullptr)//插入根節點{_root = new node(x, v);return true;}node* cur = _root;node* parent = nullptr;//保留父親節點while (cur){/*介質不冗余*/if (x < cur->_key){parent = cur;cur = cur->left;}else if (x > cur->_key){parent = cur;cur = cur->right;}else{return false;}//介質冗余//if (x <= cur->_key)//相等插入到左子樹//{//	parent = cur;//	cur = cur->left;//}//else if (x > cur->_key)//{//	parent = cur;//	cur = cur->right;//}}cur = new node(x, v);if (x > parent->_key){parent->right = cur;}else//相等插入左子樹{parent->left = cur;}cur->parent = parent;while (parent){// 更新平衡因?if (cur == parent->left)parent->bf--;elseparent->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 && cur->bf == -1){RoRateR(parent);}else if (parent->bf == 2 && cur->bf == 1){RoRateL(parent);}else if (parent->bf == -2 && cur->bf == 1){RoRateLR(parent);}else if (parent->bf == 2 && cur->bf == -1){RoRateRL(parent);}else{assert(false);}break;}else{assert(false);}}return true;
}

3.7AVL樹的查找

在避免了二叉搜索樹退化為單叉樹的情況。
AVL樹的查找效率為O(logN).

四.AVL樹的檢測

4.1AVL樹檢測

AVL樹我們可以遞歸檢測每顆子樹的左右高度差是否不差過1即可。

void Inorder()
{_Inorder(_root);cout << endl;
}
bool IsBalanceTree()
{return _IsBalanceTree(_root);
}
bool _IsBalanceTree(const node* root)
{// 空樹也是AVL樹if (nullptr == root)return true;// 計算pRoot結點的平衡因?:即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->_value << "高度差異常" << endl;return false;}if (root->bf != diff){cout << root->_key << "平衡因子異常" << endl;return false;}// pRoot的左和右如果都是AVL樹,則該樹?定是AVL樹return _IsBalanceTree(root->left) && _IsBalanceTree(root->right);
}
void _Inorder(const node* root)
{if (root == nullptr){return;}_Inorder(root->left);cout << root->_key << ":" << root->_value<<endl;_Inorder(root->right);
}
size_t Size()
{return _Size(_root);
}
size_t _Size(const node* parent)
{if (parent){return 1 + _Size(parent->left)+ _Size(parent->right);}else{return 0;}
}
size_t Height()
{return _Height(_root);
}
size_t _Height(const node* parent)
{if (parent){return 1 + max(_Height(parent->left), _Height(parent->right));}else{return 0;}
}

4.2 AVL樹的驗證

  • 檢測一
void TestAVLTree1()
{AVL::AVLTree<int, int>t;// 常規的測試?例//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 = 100000;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();AVL::AVLTree<int, int> t;for (auto e : v){t.Insert(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;
}

后言

這就是AVL樹的深度剖析。大家自己好好消化!今天就分享到這!感謝各位的耐心垂閱!咱們下期見!拜拜~

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/913339.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/913339.shtml
英文地址,請注明出處:http://en.pswp.cn/news/913339.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

支持向量機(SVM)在肝臟CT/MRI圖像分類(肝癌檢測)中的應用及實現

&#x1f9d1; 博主簡介&#xff1a;CSDN博客專家、CSDN平臺優質創作者&#xff0c;高級開發工程師&#xff0c;數學專業&#xff0c;10年以上C/C, C#, Java等多種編程語言開發經驗&#xff0c;擁有高級工程師證書&#xff1b;擅長C/C、C#等開發語言&#xff0c;熟悉Java常用開…

DeepSeek掃雷游戲網頁版HTML5(附源碼)

用DeepSeek幫忙生成一個網頁版的掃雷游戲&#xff0c;效果非常棒&#xff0c;基于HTML5實現&#xff0c;方便運行。 提示詞prompt 幫我做一個網頁版的 html5 掃雷游戲游戲功能說明 游戲難度&#xff1a; 1 簡單&#xff1a;1010 格子&#xff0c;10個地雷 2 中等&#xff1a;16…

Day53GAN對抗生成網絡思想

生成對抗網絡&#xff08;GAN&#xff09;是深度學習領域的一種革命性模型&#xff0c;由Ian Goodfellow等人于2014年提出。其核心思想源于博弈論中的零和博弈&#xff0c;通過兩個神經網絡&#xff08;生成器和判別器&#xff09;的對抗性訓練&#xff0c;實現數據的高質量生成…

meilisearch-輕量級搜索引擎

meilisearch是一款開源的輕量級搜索引擎&#xff0c;相比于elasticsearch等重量級搜索引擎&#xff0c;meilisearch注重數據搜索&#xff0c;從而而省去了其它不必要的功能&#xff08;如支持聚合分析、分布式搜索等特性&#xff09;&#xff0c;以便于快速上手開發和構建應用。…

51c大模型~合集150

我自己的原文哦~ https://blog.51cto.com/whaosoft/14034001 #原來Scaling Law還能被優化 Meta這招省token又提效 2017 年&#xff0c;一篇《Attention Is All You Need》論文成為 AI 發展的一個重要分水嶺&#xff0c;其中提出的 Transformer 依然是現今主流語言模型…

每天一個前端小知識 Day 23 - PWA 漸進式 Web 應用開發

PWA 漸進式 Web 應用開發&#xff08;離線緩存、桌面安裝等&#xff09; &#x1f9e0; 一、什么是 PWA&#xff1f; PWA&#xff08;Progressive Web App&#xff09;是一種讓 Web 應用具有類似原生 App 用戶體驗的技術體系。 PWA 不是一個框架&#xff0c;而是由一組瀏覽器 A…

音視頻會議服務搭建(設計方案-兩種集成方案對比)-03

前言在開始計劃之前&#xff0c;查閱了不少資料。一種方案是 Go層做信令業務&#xff0c;nodejs層來管理和mediasoup的底層交互&#xff0c;通過客戶端去調用Go層&#xff1b;第二種方案是 客戶端直接調用nodejs層來跟mediasoup去交互&#xff1b; 最終&#xff0c;當然不出意料…

【小白】linux安裝ffmpeg | java轉碼 【超詳細】

前言 最近在開發過程中&#xff0c;發現當我們上傳除了mp4以外的其他少見的格式&#xff0c;如 .flv .rmvb 格式的視頻時&#xff0c;在前端在線播放的時候會播放不出來畫面&#xff0c;所以 接下來&#xff0c;將要進行一個非常完美的工程&#xff0c;將視頻格式轉為.mp4 1.安…

一個簡單的腳本,讓pdf開啟夜間模式

因為平常我比較喜歡晚上看面試題。 市面上很多的面試題pdf都是白色的晚上看的話非常的刺眼。 所以我本能的去互聯網搜索看看有沒有pdf轉換為夜間模式的。 搜索了一段時間后發現并沒有這種東西。于是我自己做了一個轉換的python腳本。 import os import fitz # PyMuPDF from P…

Flink OceanBase CDC 環境配置與驗證

一、OceanBase 數據庫核心配置 1. 環境準備與版本要求 版本要求&#xff1a;OceanBase CE 4.0 或 OceanBase EE 2.2組件依賴&#xff1a;需部署 LogProxy 服務&#xff08;社區版/企業版部署方式不同&#xff09;兼容模式&#xff1a;支持 MySQL 模式&#xff08;默認&#x…

c++對象池

【設計模式】其它經典模式-對象池模式&#xff08;Object Pool Pattern&#xff09;-CSDN博客 在C中&#xff0c;對象池&#xff08;Object Pool&#xff09;是一種管理對象生命周期的技術&#xff0c;旨在減少對象創建和銷毀的開銷&#xff0c;提高性能。對象池預先分配一定數…

JavaFX:Scene(場景)

簡介 Scene對象是JavaFX場景圖的根(root)。JavaFX 場景中包含所有可視的 JavaFX GUI 組件。JavaFX 場景由javafx.scene.Scene類表示。必須在 Stage(舞臺)上設置 Scene 對象才能使其可見。在本 JavaFX Scene 教程中,將向您展示如何創建 Scene 對象并向其添加 GUI 組件。 創…

vue3.4中的v-model的用法~

1.首先以前我們針對父子組件傳參是不是通過defineProps與defineEmits來實現的&#xff0c;但是這么比較繁瑣&#xff0c;因為他是單向傳參&#xff0c;而不是雙向的&#xff0c;這里我們要介紹的是vue3.4的v-model來實現雙向數據傳遞。 2、代碼示例&#xff1a; //父組件 <…

nvm常用指令匯總

nvm是用來管理nodejs的&#xff0c;可以方便安裝、切換、卸載當前環境的node版本。 以下是常用指令匯總&#xff1a;nvm list 查看本機已經安裝的node版本。*表示當前系統正在使用的node版本nvm install xx.xx.x 后邊加版本號&#xff0c;表示安裝指定的版本nvm use xx.xx.x當前…

洛谷P5021 [NOIP 2018 提高組] 賽道修建【題解】【二分答案+樹上貪心】

P5021 [NOIP 2018 提高組] 賽道修建 題意簡述 給定一棵含 n n n 個點的無向帶權樹&#xff0c;求將其分裂為 m m m 條鏈后&#xff0c;最短的一條鏈的最大長度是多少&#xff1f; 點可以重復使用&#xff0c;邊不可以重復使用。 思路 二分答案貪心判定貌似可以&#xff…

Portal認證過程雜談

Portal認證模型簡介 Portal認證模型通常由這四個設備組成 認證服務器即3A服務器&#xff0c;通常用radius服務器 接入設備通常就是NAC設備&#xff08;網絡接入控制&#xff09; Portal服務器就是Portal認證的認證網站&#xff08;通常叫門戶網站&#xff09; 認證過程簡述…

ZSGuardian ---AI賦能,新一代研發管理守護平臺 -即將上線

一場研發管理的革命 在數字化浪潮奔涌向前的今天&#xff0c;軟件開發與產品研發的節奏不斷加快&#xff0c;市場需求瞬息萬變&#xff0c;技術迭代日新月異。對于研發團隊而言&#xff0c;如何在復雜多變的環境中&#xff0c;高效地管理項目、保障產品質量、確保按時上線&…

小菜狗的云計算之旅,學習了解rsync+sersync實現數據實時同步(詳細操作步驟)

Rsyncsersync實現數據實時同步 目錄 Rsyncsersync實現數據實時同步 一、rsync概述 二、rsync運行原理 三、rsync部署 四、備份測試 五、使用非系統用戶備份數據 5.1 rsync的配置文件介紹 5.2 配置備份目錄 5.3 使用rsync用戶備份測試 5.4 pull拉取數據 六、rsyncse…

牛客周賽Round 99(Go語言)

A題 (A.go) 思路總結: 這道題要求判斷一個整數中是否包含連續的兩個9。 核心思路是將輸入的整數轉換為字符串&#xff0c;然后遍歷這個字符串&#xff0c;檢查是否存在相鄰的兩個字符都是9。如果找到了&#xff0c;就立即停止遍歷并輸出"YES"&#xff1b;如果遍歷完…

紅外圖像小目標檢測熱力圖可視化系統

原創代碼&#xff0c;可以工程修改含界面。