AVL平衡二叉樹

01. 初始AVL樹

AVL樹是最早發明的自平衡二叉搜索樹。在AVL樹中,任何節點的兩個子樹的高度差(平衡因子)最多為1,這使得AVL樹能夠保持較好的平衡性,從而保證查找、插入和刪除操作的時間復雜度都是O(log n)。

在這里插入圖片描述

包含n個節點的AVL樹的高度始終保持在O(log n)


02. AVL樹的結構

AVL 樹在原 二叉搜索樹 的基礎上添加了 平衡因子 bf 以及用于快速向上調整的 父親指針 parent,所以 AVL 樹是一個三叉鏈結構。

在這里插入圖片描述

2.1 AVL樹節點定義

template <class K, class V>
struct AVLTreeNode // 定義借點類型
{AVLTreeNode(const std::pair<K, V> kv): _parent(nullptr), _left(nullptr), _right(nullptr), _kv(kv), _bf(0){}AVLTreeNode<K, V> *_parent;AVLTreeNode<K, V> *_left;AVLTreeNode<K, V> *_right;//這里并沒有直接存儲數據K,和V,而是像map那樣將其封裝成一個pair<K,V>類型。std::pair<K, V> _kv; // 存儲鍵值對int _bf;             // 平衡因子
};

那么在AVLTree類方法實現只需創建一個AVLTreeNode<K,V>*類型的_root根節點。

規定: 當前實現的平衡因子,規定差值為 右 - 左,因此如果右子樹增高,_bf++,左子樹增高 _bf–,具體操作將在后面體現。


2.2 AVL樹的方法

template<class K,class V>
class AVLTree{typedef TreeNode<K, V> Node;
public://插入bool insert(const pair<K, V> kv) {}//查找bool find(const K& kev) {}//中序遍歷void order() {}void RotateR(Node* parent) {}	//右單旋void RotateL(Node* parent) {}	//左單旋void RRotateRL(Node* parent) {}	//右左雙選void RotateLR(Node* parent) {}	//左右雙選void order(Node* root) {}	//中序遍歷private:Node* _root;
};

03. AVL樹的插入

3.1插入過程

對于avl樹的插入,實際是在二叉搜索樹基礎之上,增加了更新平衡因子和在這個過程中進行旋轉調整的過程。

  1. 空樹檢查
    • 若樹為空 (_root == nullptr),直接創建新節點作為根節點,返回 true
  2. 搜索插入位置
    • 從根節點開始比較 key
      • key < 當前節點:向左子樹搜索
      • key > 當前節點:向右子樹搜索
      • key == 當前節點:鍵已存在,返回 false
  3. 插入新節點
    • 創建新節點并鏈接到父節點:
      • key < 父節點:作為左孩子插入
      • key > 父節點:作為右孩子插入
  4. 平衡因子更新與調整(下面完成)
  • 更新平衡因子,然后判斷是否需要進行旋轉調整高度

bool Insert(const std::pair<K, V> kv){if (_root == nullptr){                         // 樹中沒有任何節點,直接new_root = new Node(kv); // 未使用Node(key, value),而是采用簡直對的形式return true;}// 不為空樹時的操作,找到插入點Node *parent = nullptr;Node *cur = _root;while (cur){if (kv.first < cur->_kv.first){ // 左走parent = cur;cur = cur->_left;}else if (kv.first > cur->_kv.first){parent = cur;cur = cur->_right;}elsereturn false;} // 退出的時候找到,即找到葉節點仍需要判斷往那邊走,插入數據(cur==null)// 創建新節點插入cur = new Node(kv);if (kv.first < parent->_kv.first0){parent->_left = cur;cur->_parent = parent;}else{parent->_right = cur;cur->_parent = parent;}//控制平衡//1、更新平衡因子----更新新增節點到根節點的祖先路徑//2、出現異常平衡因子,那么需要旋轉平衡處理while (parent) {if (cur == parent->_right)parent->_bf++;elseparent->_bf--;if (parent->_bf == 0) {break;}else if (abs(parent->_bf) == 1) {//繼續往上更新cur = parent;parent = parent->_parent;}else if (abs(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);//右左雙旋}else{assert(false);}break;}else {//說明插入更新平衡因子之前,樹中平衡因子就有問題了assert(false);}} return true;}

3.1 左單旋

左單旋適用場景:

  P (_bf=+2)\							    C (_bf=0)C (_bf=+1)     調整為    		 /	   \\						     P       RR							  (_bf=0)

在這里插入圖片描述

當插入10的時候出現不平衡。

左單旋:

  1. 確定parentsubRsubRL
  2. 使subRL成為parent右孩子
  3. subR左孩子變為parent
  4. 注意pparent的更新,和paarent為根節點情況
    void RotateL(Node *parent){ // parent在_bf=2處Node *subR = parent->_right;Node *subRL = subR->_left;// 先將subR的左孩子交給parentparent->_right = subRL;if (subRL != nullptr){subRL->_parent = parent;}Node *pparent = parent->_parent;//保存parent的父節點,確保更新平衡因子subR->_left = parent;parent->_parent = subR;if (_root == parent){//是否是根節點subR->_parent = nullptr;_root = subR;}else{if (pparent->_right == parent){pparent->_right = parent;}else{pparent->_left = subR;}subR->_parent = pparent;}parent->_bf = subR->_bf = 0;}
注意事項
  1. 空指針檢查
    • subRL 可能為 nullptr,需判斷后再操作其父指針
  2. 根節點更新
    • parent 是根節點,旋轉后需更新 _root 指向 subR
  3. 平衡因子重置
    • 左單旋后,parentsubR 的平衡因子必為 0

3.2 右單旋

          P (_bf=-2)/						   C (_bf=0)C (_bf=-1)				 /	   \/						L	   PL (_bf=0)					  (_bf=-0)

在這里插入圖片描述

當插入10的時候出現不平衡。

右單旋:

  1. 確定parentsubLsubLR
  2. 使subLR成為parent左孩子
  3. subR右孩子變為parent
  4. 注意pparent的更新,和paarent為根節點情況
//右單旋
void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;//先將 subL 的右孩子移交給父親parent->_left = subLR;if (subLR != nullptr)subLR->_parent = parent;Node* pparent = parent->_parent;subL->_right = parent;parent->_parent = subL;//再將父親移交給 subL,subL 成為新父親if (parent == _root){//如果原父親為根,那么此時需要更新 根subL->_parent = nullptr;_root = subL;}else{//單純改變鏈接關系if (pparent->_right == parent)pparent->_right = subL;elsepparent->_left = subL;subL->_parent = pparent;}//更新平衡因子parent->_bf = subL->_bf = 0;
}
注意事項
  1. 空指針檢查
    • subLR 可能為 nullptr,需判斷后再操作其父指針。
  2. 根節點更新
    • parent 是根節點,旋轉后需更新 _root 指向 subL
  3. 平衡因子重置
    • 右單旋后,parentsubL 的平衡因子必為 0

3.3 右左單旋

在這里插入圖片描述

//右左雙旋
void RotateRL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;int BF = subRL->_bf;//先右單旋RotateR(subR);//再左單旋RotateL(parent);//根據不同的情況更新平衡因子if(BF == 0){parent->_bf = subR->_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{//非法情況std::cerr << "此處的平衡因子出現異常!" << std::endl;assert(false);	//直接斷言報錯}
}

右左單旋:

  1. 右單旋:
    1. 確定 parentsubRsubRL
    2. subRL的右子樹變為 subR左子樹,parent右子樹為subRL左子樹
    3. subRL向上提
平衡因子調整(分三類)
情況subRL->_bf調整后平衡因子觸發條件
情況10parent = subR = subRL = 0新節點是 subRL 自身
情況2-1parent = 0, subR = +1, subRL = 0新節點在 subRL 左子樹
情況3+1parent = -1, subR = 0, subRL = 0新節點在 subRL 右子樹

3.4 左右雙旋

右左雙旋左右雙旋邏輯非常像,這里就不演示了,直接看代碼實現:

//左右雙旋
void RotateLR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;int BF = subLR->_bf;//先左單旋RotateL(subL);//再右單旋RotateR(parent);//根據不同的情況更新平衡因子if (BF == 0){parent->_bf = subL->_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{//非法情況std::cerr << "此處的平衡因子出現異常!" << std::endl;assert(false);	//直接斷言報錯}
}

3.5 AVL查找

	//查找bool find(const K& kv) {Node* ptail = _root;while (ptail){if (kv.first > ptail->_kv->first){ptail = ptail->_right;}else if (kv.first < ptail->_kv->first){ptail = ptail->_left;}else{return true;}}return false;}

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

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

相關文章

教育行業可以采用Html5全鏈路對視頻進行加密?有什么優勢?

文章目錄前言一、什么是Html5加密&#xff1f;二、使用Html5對視頻加密的好處三、如何采用Html5全鏈路對視頻進行加密&#xff1f;四、教育行業采用Html5全鏈路視頻加密有什么優勢&#xff1f;總結前言 面對優質課程盜錄傳播的行業痛點&#xff0c;教育機構如何守護核心知識產…

Vue3 tailwindcss

1、安裝tailwindcsspnpm i -D tailwindcss postcss autoprefixer # yarn add -D tailwindcss postcss autoprefixer # npm i -D tailwindcss postcss autoprefixer2、 創建TailwindCSS配置文件npx tailwindcss init -ptailwind.config.js/** type {import(tailwindcss).Config}…

提示工程:解鎖大模型潛力的核心密碼

以下是對Lilian Weng的提示工程權威指南&#xff08;原文鏈接&#xff09;的深度解析與博客化重構&#xff0c;融入最新行業實踐&#xff1a; 提示工程&#xff1a;解鎖大模型潛力的核心密碼 ——從基礎技巧到工業級解決方案全解析 一、重新定義人機交互范式 傳統編程 vs 提示…

Python3郵件發送全指南:文本、HTML與附件

在 Python3 中&#xff0c;使用內置的 smtplib 庫和 email 模塊發送郵件是一個常見的需求。以下是更詳細的實現指南&#xff0c;包含各種場景的解決方案和技術細節&#xff1a;一、發送純文本郵件的完整實現準備工作&#xff1a;確保已開通 SMTP 服務&#xff08;各郵箱開啟方式…

CSS和CSS3區別對比

CSS&#xff08;層疊樣式表&#xff09;與CSS3&#xff08;CSS的第三個版本&#xff09;的區別主要體現在功能擴展、語法特性以及應用場景等方面。以下是兩者的核心對比&#xff1a; 一、核心概念與版本關系CSS&#xff1a;是基礎樣式表語言&#xff0c;用于分離網頁內容與樣式…

JVM--監控和故障處理工具

一、命令行工具 1. jps (Java Process Status) 作用&#xff1a;列出當前系統中所有的 Java 進程 常用命令&#xff1a; jps -l # 顯示進程ID和主類全名 jps -v # 顯示JVM啟動參數 輸出示例&#xff1a; 1234 com.example.MainApp 5678 org.apache.catalina.startup.Bootstra…

推薦 7 個本周 yyds 的 GitHub 項目。

01.開源的 CRM 軟件這是一個開源的客戶關系管理&#xff08;CRM&#xff09;系統&#xff0c;現在又 32.5K 的 Star。為企業和團隊提供比肩 Salesforce 等商業產品的功能&#xff0c;同時強調用戶自主權、數據自由與高度可定制性。開源地址&#xff1a;https://github.com/twen…

linux網絡編程之單reactor模型(一)

Reactor 是一種事件驅動的設計模式&#xff08;Event-Driven Pattern&#xff09;&#xff0c;主要用于處理高并發 I/O&#xff0c;特別適合網絡服務器場景。它通過一個多路復用機制監聽多個事件源&#xff08;如 socket 文件描述符&#xff09;&#xff0c;并在事件就緒時將事…

瀏覽器重繪與重排

深入解析瀏覽器渲染&#xff1a;重排(Reflow)與重繪(Repaint)的性能陷阱與優化策略作為一名前端開發者&#xff0c;你是否遇到過界面突然卡頓、滾動時頁面抖動或輸入框響應遲鈍&#xff1f;這些常見性能問題背后&#xff0c;往往是重排與重繪在作祟。本文將深入剖析瀏覽器渲染機…

day049-初識Ansible與常用模塊

文章目錄0. 老男孩思想-人脈的本質1. Ansible1.1 密鑰認證1.2 安裝ansible1.3 添加ansible配置文件1.4 配置主機清單文件&#xff08;Inventory&#xff09;1.5 測試1.6 ansible的模塊思想1.7 command模塊1.8 需求&#xff1a;每臺服務器的密碼都不同&#xff0c;怎么批量執行業…

力扣網編程134題:加油站(雙指針)

一. 簡介 前面兩篇文章使用暴力解法&#xff0c;或者貪心算法解決了力扣網的加油站問題&#xff0c;文章如下&#xff1a; 力扣網編程150題&#xff1a;加油站&#xff08;暴力解法&#xff09;-CSDN博客 力扣網編程150題&#xff1a;加油站&#xff08;貪心解法&#xff09…

XPath 語法【Web 自動化-定位方法】

&#x1f9ed; XPath 語法簡介&#xff08;Web 自動化核心定位手段&#xff09;一、XPath 是什么&#xff1f;XPath&#xff08;XML Path Language&#xff09;是用于在 XML/HTML 文檔中定位節點的語言&#xff0c;由 W3C 標準定義。瀏覽器支持的是 XPath 1.0。應用場景廣泛&am…

記一次 Linux 安裝 docker-compose

一.下載 1.手動下載 下載地址&#xff1a;https://github.com/docker/compose/releases 下載后&#xff0c;放在/usr/local/bin/目錄下&#xff0c;命名為&#xff1a;docker-compose 2.命令下載 sudo curl -L "https://github.com/docker/compose/releases/download/…

Go語言WebSocket編程:從零打造實時通信利器

1. WebSocket的魅力&#xff1a;為什么它這么火&#xff1f;WebSocket&#xff0c;簡單來說&#xff0c;就是一種在單條TCP連接上實現全雙工通信的神器。相比HTTP的請求-響應模式&#xff0c;它像是一條隨時暢通的電話線&#xff0c;客戶端和服務器可以隨時“喊話”&#xff0c…

速學 RocketMQ

目錄 本地啟動&測試&可視化 核心概念 集群 主從 集群 Dledger 集群 總結 客戶端消息確認機制 廣播模式 消息過濾機制 順序消息機制 延遲消息與批量消息 事務消息機制 ACL權限控制體系 RocketMQ客戶端注意事項 消息的 ID、Key、Tag 最佳實踐 消費者端…

【個人思考】不點菜的美學:Omakase 的信任、四季與食藝

本文原創作者:姚瑞南 AI-agent 大模型運營專家/音樂人/野生穿搭model,先后任職于美團、獵聘等中大廠AI訓練專家和智能運營專家崗;多年人工智能行業智能產品運營及大模型落地經驗,擁有AI外呼方向國家專利與PMP項目管理證書。(轉載需經授權) 目錄 ?? 什么是 Omakase?…

vivo Pulsar 萬億級消息處理實踐(3)-KoP指標異常修復

作者&#xff1a;vivo 互聯網大數據團隊- Chen Jianbo 本文是《vivo Pulsar萬億級消息處理實踐》系列文章第3篇。 Pulsar是Apache基金會的開源分布式流處理平臺和消息中間件&#xff0c;它實現了Kafka的協議&#xff0c;可以讓使用Kafka API的應用直接遷移至Pulsar&#xff0c;…

Marin說PCB之Allegro高亮BOM器件技巧詳解

一&#xff0c;首先在原理圖輸出BOM的時候&#xff0c;只需要勾選器件的位號這個選項即可&#xff0c;具體操作如下所示&#xff1a;二&#xff0c;輸出BOM完成后&#xff0c;打開表格選擇我們器件的位號那列即可&#xff0c;然后復制到我們的TEXT文本中。三&#xff0c;接著就…

數據結構與算法——從遞歸入手一維動態規劃【2】

前言&#xff1a; 記錄一下對左程云系列算法課程--算法講解066【必備】的剩余習題的學習。本文主要簡單記錄個人學習心得和提供C版本代碼。如需要題目的細致講解&#xff0c;請前往原視頻。 涉及內容&#xff1a; 動態規劃、三指針、 參考視頻&#xff1a; 左程云--算法講…

【理念●體系】Windows AI 開發環境搭建實錄:六層架構的逐步實現與路徑治理指南

【理念●體系】從零打造 Windows WSL Docker Anaconda PyCharm 的 AI 全鏈路開發體系-CSDN博客 Windows AI 開發環境搭建實錄&#xff1a;六層架構的逐步實現與路徑治理指南 ——理念落地篇&#xff0c;從路徑規劃到系統治理&#xff0c;打造結構化可復現的 AI 開發環境 AI…