深入理解AVL樹及其旋轉操作

AVL樹的概念

二叉搜索樹雖可以縮短查找的效率,但如果數據有序或接近有序二叉搜索樹將退化為單枝樹,查找元素相當于在順序表中搜索元素,效率低下。因此,兩位俄羅斯的數學家G.M.Adelson-Velskii和E.M.Landis在1962年發明了一種方法:當二叉樹搜索樹中插入新節點后,如果能保證每個系欸但的左右子樹的高度之差的絕對值不超過1,也就是說要降低樹的高度,從而減少平均搜索長度。

一棵AVL樹,它具有以下的性質:

  • 它的左右子樹都是AVL樹
  • 左右子樹高度之差的絕對值不超過1

如果一棵搜索二叉樹的高度是平衡的,它就是AVL樹。如果它有n個結點,其高度可保持在log2(n),搜索時間復雜度log2(n)。

AVL樹結點的定義

struct AVLTreeNode
{AVLTreeNode<K, V>* _left;//左孩子AVLTreeNode<K, V>* _right;//右孩子AVLTreeNode<K, V>* _parent;//父節點std::pair<K, V> _kv;//鍵值對int _bf;//平衡因子AVLTreeNode(const std::pair<K, V>& kv) : _left(nullptr),_right(nullptr),_parent(nullptr),_kv(kv),_bf(0){}
};

這里定義了對AVL樹的結點的描述,一個結點應當有左右孩子結點,父節點,鍵值對,以及平衡因子
左右孩子結點和鍵值對是基于搜索二叉樹(AVL樹是在搜索二叉樹的基礎上建立的)。
平衡因子用來衡量這顆樹是否是平衡的。
平衡因子=右子樹高度-左子樹高度
至于父節點,則是未來方便平衡因子的更新而添加進去的。

AVL樹的插入

AVL樹是在搜索二叉樹的基礎上引入了平衡因子,因此AVL樹也可以看成是二叉搜索樹。所以,插入的過程可以分為兩步:

  1. 按照搜索二叉樹的方式插入新節點
  2. 調整結點的平衡因子
bool Insert(const std::pair<K,V>&kv)
{Node* cur = _root;Node* parent = nullptr;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 (parent == nullptr){_root = cur;return true;}if (parent->_kv.first < kv.first){parent->_right = cur;}elseparent->_left = cur;cur->_parent = parent;return true;
}

按照搜索二叉樹的規則,查找到我們需要插入的地方。

  • 插入的鍵比當前結點的鍵小,遞歸左子樹
  • 插入的鍵比當前節點的鍵大,遞歸右子樹
  • 插入的鍵等于當前節點的鍵,說明已經插入過了,本次插入是失敗的
    然后開始new一個新的結點,把結點之前的父子關系處理好

AVL樹的調整(旋轉)

如果在一棵原本是平衡的AVL樹中插入一個新的結點,可能會造成不平衡的情況,此時就必須要調整A樹的結構,使其平衡話。AVL樹的旋轉大致分為四種,這里采用畫圖的方式來理解這四種旋轉的方式:

左左:右單旋

在這里插入圖片描述

右右:左單旋

在這里插入圖片描述

左右:先左旋,后右旋

在這里插入圖片描述
這里的插入情況不唯一,但是值得一提的是
在調整之后,subLR的左子樹一定是充當subL的右子樹,subLR的右子樹一定是充當了parent的左子樹,可以根據這個來計算平衡因子

右左:先右旋,后左旋

在這里插入圖片描述
同左右情況,這里也有相似的結論:
再調整之后,subRL的左子樹充當為parent的右子樹,subRL的右子樹充當為subR的左子樹

AVL樹的完整實現代碼

#include <iostream>
#include <cassert>template <class K, class V>
struct AVLTreeNode
{AVLTreeNode<K, V>* _left;AVLTreeNode<K, V>* _right;AVLTreeNode<K, V>* _parent;std::pair<K, V> _kv;int _bf;//平衡因子AVLTreeNode(const std::pair<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 std::pair<K,V>&kv){Node* cur = _root;Node* parent = nullptr;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 (parent == nullptr){_root = cur;return true;}if (parent->_kv.first < kv.first){parent->_right = cur;}elseparent->_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){//說明之前是0,需要繼續向上更新cur = parent;parent = parent->_parent;}else if (parent->_bf == 2 || parent->_bf == -2){//這里需要進行旋轉平衡if (parent->_bf == -2 && cur->_bf == -1){RotateR(parent);}else if (parent->_bf == 2 && cur->_bf == 1){RotateL(parent);}else if (parent->_bf == -2 && cur->_bf == 1){RotateLR(parent);}else if (parent->_bf == 2 && cur->_bf == -1){RotateRL(parent);}break;}else {//理論上不可能出現這種情況assert(false);}}return true;}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;}void InOrder(){_InOrder(_root);}//右旋轉void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;subL->_right = parent;Node* ppNode = parent->_parent;parent->_parent = subL;//整棵樹的根節點if (parent == _root){_root = subL;_root->_parent = nullptr;}//某個子樹的根節點else{subL->_parent = ppNode;if (ppNode->_left == parent){ppNode->_left = subL;}else {ppNode->_right = subL;}}parent->_bf = subL->_bf = 0;}//左單旋void RotateL(Node*parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;subR->_left = parent;Node* ppNode = parent->_parent;parent->_parent = subR;if (parent == _root){_root = subR;_root->_parent = nullptr;}else {subR->_parent = ppNode;if (parent == ppNode->_left){ppNode->_left = subR;}else {ppNode->_right = subR;}}parent->_bf = subR->_bf = 0;}void RotateRL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;int bf = subRL->_bf;RotateR(parent->_right);RotateL(parent);if (bf == 1){subR->_bf = 0;subRL->_bf = 0;parent->_bf = -1;}else if (bf == -1){subR->_bf = 1;subRL->_bf = 0;parent->_bf = 0;}else if (bf == 0){subR->_bf = 0;subRL->_bf = 0;parent->_bf = 0;}else{assert(false);}}void RotateLR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;int bf = subLR->_bf;RotateL(parent->_left);RotateR(parent);if (bf == -1){//說明是在subLR的左邊插入subL->_bf = 0;subLR->_bf = 0;parent->_bf = 1;}else if (bf == 1){//說明是在subLR的右邊插入subL->_bf = -1;subLR->_bf = 0;parent->_bf = 0;}else if (bf == 0) {subL->_bf = 0;subLR->_bf = 0;parent->_bf = 0;}else{assert(false);}}bool IsBalance(){return _IsBalance(_root);}int Height(){return _Height(_root);}
private:bool _IsBalance(Node*node){if (node == nullptr)return true;int leftHeight = _Height(node->_left);int rightHeight = _Height(node->_right);if (abs(leftHeight - rightHeight) >= 2)return false;//檢查一下平衡因子是否是正確的if (rightHeight - leftHeight != node->_bf){std::cout << node->_kv.first << std::endl;return false;}return _IsBalance(node->_left) &&_IsBalance(node->_right);}int _Height(Node* node){if (node == nullptr)return 0;return std::max(_Height(node->_left), _Height(node->_right)) + 1;}void _InOrder(Node* node){//中序遍歷if (node == nullptr)return;_InOrder(node->_left);std::cout << node->_kv.first << ": " << node->_kv.second << std::endl;_InOrder(node->_right);}
private:Node* _root = nullptr; // 根節點
};

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

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

相關文章

URL帶有中文會引入哪些問題

處理含中文字符的 URL 1 為什么會出現“亂碼”或崩潰&#xff1f; URL 標準&#xff08;RFC 3986&#xff09;規定&#xff1a;除少數保留字符外&#xff0c;URL 只能包含 ASCII。中文屬于 Unicode&#xff0c;因此必須先轉換。如果直接把 https://example.com/路徑/ 這樣的字…

結構體字段能否單獨加 mut

你問的這個問題在 Rust 里很常見&#xff1a; 一、結構體字段能否單獨加 mut 1. 結構體字段能否單獨加 mut&#xff1f; 不能。Rust 中&#xff0c;mut 是用來修飾變量綁定的&#xff0c;可變性是綁定的屬性&#xff0c;而不是結構體字段本身的屬性。 你不能寫&#xff1a; …

scGPT-spatial 復現

文章目錄 ? 總體流程總覽&#xff08;從 H5AD 到模型訓練&#xff09;&#x1f527; 步驟 1&#xff1a;讀取 H5AD 文件并做基礎預處理&#x1f9f1; 步驟 2&#xff1a;構造訓練樣本輸入&#xff08;token、value&#xff09;&#x1f4e6; 步驟 3&#xff1a;使用 DataColla…

運放電壓跟隨器為什么要加電阻

運放電壓跟隨器為什么要加電阻 我們常見運放的電壓跟隨器如下&#xff1a; 有時候會看見電路中加兩個電阻&#xff1a; 作用就是保護運放&#xff0c;起限流電阻的作用。 當輸入電壓高的時候&#xff0c;運放內部存在鉗位二極管&#xff0c;此電阻就能限流。 并不是所有運放…

MinerU 2.0部署

簡介 MinerU 2.0使用sglang加速&#xff0c;與之前差別較大&#xff0c;建議按照官方的Docker鏡像的方式啟動。 Docker鏡像 Dockerfile 這是官方的Dockerfile # Use the official sglang image FROM lmsysorg/sglang:v0.4.7-cu124# install mineru latest RUN python3 -m …

黑馬python(十七)

目錄&#xff1a; 1.數據可視化-地圖-基礎案例 2.全國疫情地圖 3.河南省疫情地圖繪制 4.基礎柱狀圖構建 5.基礎時間線柱狀圖繪制 6.動態GDP柱狀圖繪制 1.數據可視化-地圖-基礎案例 圖示有點對的不準&#xff0c;可以通過后面的參數 2.全國疫情地圖 3.河南省疫情地圖繪制…

Segment Anything in High Quality之SAM-HQ論文閱讀

摘要 最近的 Segment Anything Model(SAM)在擴展分割模型規模方面取得了重大突破,具備強大的零樣本能力和靈活的提示機制。盡管 SAM 在訓練時使用了 11 億個掩碼,其掩碼預測質量在許多情況下仍不理想,尤其是對于結構復雜的目標。我們提出了 HQ-SAM,使 SAM 能夠精確地分割…

深入理解_FreeRTOS的內部實現(2)

1.事件組 事件組結構體&#xff1a; 事件組 “不關中斷” 的核心邏輯 事件組操作時&#xff0c;優先選擇 “關調度器” 而非 “關中斷” &#xff0c;原因和實現如下&#xff1a; 關調度器&#xff08;而非關中斷&#xff09; FreeRTOS 提供 taskENTER_CRITICAL()&#xff08;…

【圖論題典】Swift 解 LeetCode 最小高度樹:中心剝離法詳解

文章目錄 摘要描述題解答案題解代碼分析思路來源&#xff1a;樹的“中心剝離法”構造鄰接表和度數組循環剝葉子終止條件 示例測試及結果時間復雜度空間復雜度總結 摘要 樹是一種重要的數據結構&#xff0c;在許多應用里&#xff0c;我們希望選一個根&#xff0c;讓這棵樹的高度…

Docker的介紹與安裝

? Docker 對初學者的簡單解釋和應用場景 1.什么是 Docker&#xff1f; 簡單來說&#xff0c;Docker 就像一個“裝箱子”的工具&#xff0c;這個箱子叫做“容器”。 你寫的程序和它運行需要的環境&#xff08;比如操作系統、軟件、工具&#xff09;都裝進一個箱子里。這個箱…

引導相機:工業自動化的智能之眼,賦能制造業高效升級

在工業自動化浪潮中&#xff0c;精準的視覺引導技術正成為生產效率躍升的關鍵。作為遷移科技——一家成立于2017年、專注于3D工業相機和3D視覺系統的領先供應商&#xff0c;我們深知"引導相機"的核心價值&#xff1a;它不僅是一個硬件設備&#xff0c;更是連接物理世…

智能相機如何重塑工業自動化?遷移科技3D視覺系統的場景革命

從硬件參數到產業價值&#xff0c;解碼高精度視覺系統的落地邏輯 一、工業視覺的“智慧之眼” 遷移科技深耕3D工業相機領域&#xff0c;以“穩定、易用、高回報”為核心理念&#xff0c;打造覆蓋硬件、算法、軟件的全棧式視覺系統。成立6年累計融資數億元的背后&#xff0c;是…

【數據挖掘】聚類算法學習—K-Means

K-Means K-Means是一種經典的無監督學習算法&#xff0c;用于將數據集劃分為K個簇&#xff08;clusters&#xff09;&#xff0c;使得同一簇內的數據點相似度高&#xff0c;不同簇間的相似度低。它在數據挖掘、模式識別和機器學習中廣泛應用&#xff0c;如客戶細分、圖像壓縮和…

linux環境內存滿php-fpm

檢查 PHP-FPM 配置 pm.max_children&#xff1a;該參數控制 PHP-FPM 進程池中最大允許的子進程數。過高的子進程數會導致內存占用過大。你可以根據服務器的內存大小來調整 pm.start_servers&#xff1a;控制 PHP-FPM 啟動時創建的進程數。根據實際情況調整此值。 pm.min_spare_…

基于CNN卷積神經網絡圖像識別小程序9部合集

基于CNN卷積神經網絡圖像識別小程序合集-視頻介紹下自取 ? 內容包括&#xff1a; 基于python深度學習的水果或其他物體識別小程序 003基于python深度學習的水果或其他物體識別小程序_嗶哩嗶哩_bilibili 代碼使用的是python環境pytorch深度學習框架&#xff0c;代碼的環境安…

WebRTC(九):JitterBuffer

JitterBuffer Jitter “Jitter”指的是連續到達的媒體包之間時間間隔的變化。在網絡傳輸中&#xff0c;由于&#xff1a; 網絡擁塞路由路徑變化隊列排隊不同鏈路帶寬差異 導致包之間的接收時間不一致&#xff0c;這就是網絡“抖動”。 作用 **JitterBuffer&#xff08;抖…

【推薦100個unity插件】在 Unity 中繪制 3D 常春藤,模擬生長——hedera插件的使用

注意&#xff1a;考慮到后續接觸的插件會越來越多&#xff0c;我將插件相關的內容單獨分開&#xff0c;并全部整合放在【推薦100個unity插件】專欄里&#xff0c;感興趣的小伙伴可以前往逐一查看學習。 效果演示 文章目錄 效果演示前言一、常春藤生成器工具下載二、工具使用1、…

【三維重建】【3DGS系列】【深度學習】3DGS的理論基礎知識之高斯橢球的幾何變換

【三維重建】【3DGS系列】【深度學習】3DGS的理論基礎知識之高斯橢球的幾何變換 文章目錄 【三維重建】【3DGS系列】【深度學習】3DGS的理論基礎知識之高斯橢球的幾何變換前言模型變換(Model Transformation)觀測變換(Viewing Transformation)視圖變換(View Transformation)投影…

EXISTS 和 NOT EXISTS 、IN (和 NOT IN)

在 SQL 中&#xff0c;EXISTS、NOT EXISTS 和 IN 都是用于子查詢的條件運算符&#xff0c;用于根據子查詢的結果過濾主查詢的行。它們之間的區別主要體現在工作方式、效率、對 NULL 值的處理以及適用場景上。 1. EXISTS 和 NOT EXISTS 作用&#xff1a; EXISTS: 檢查子查詢是…

GitHub 趨勢日報 (2025年06月25日)

&#x1f4ca; 由 TrendForge 系統生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日報中的項目描述已自動翻譯為中文 &#x1f4c8; 今日獲星趨勢圖 今日獲星趨勢圖 880 awesome 788 build-your-own-x 691 free-for-dev 427 best-of-ml-python 404 …