數據結構--AVL樹

目錄

前言

AVL樹的特點

AVL樹的插入

節點的定義

情況分析

AVL樹的旋轉

右單旋

左單旋

左右雙旋

右左雙旋

?編輯總結?

驗證AVL樹


前言

二叉搜索樹可以幫助我們以極高的效率查找(理想情況下是logn),但是當在極端情況下,比如當樹中的節點值是有序的時,二叉搜索樹會變成一個單枝樹,相當于一個鏈表,于是乎為了讓樹更接近與一個完全二叉樹,想著當向二叉搜索樹中插入新節點后,讓每個節點的左右子樹高度差的絕對值不超過1就可以降低樹的高度從而提高效率,于是就有了AVL樹。

AVL樹的特點

  • 每個節點的左右子樹都是AVL樹
  • 左右子樹高度差(平衡因子)的絕對值不超過1

AVL樹的插入

節點的定義

static class TreeNode {public int val;public TreeNode left;public TreeNode right;public int bf;//平衡因子public TreeNode parent;public TreeNode(int val) {this.val = val;}}

?//平衡因子=右子樹高度-左子樹高度

情況分析

因為AVL樹也是二叉搜索樹,所以先按照二叉搜索樹的方式插入一個節點cur,但是當cur被插入后,其父節點parent的平衡因子必定會發生變化(左右子樹高度差必定發生變化),所以每次插入節點后都需要更新平衡因子,并且檢查AVL樹的結構是否被破壞。情況可以分為以下幾種

cur插入之前parent的平衡因子會有三種可能'-1' ,'0' ,'1'

  • 當cur插入到parent的左邊時parent的平衡因子-1
  • 當cur插入到parent的右邊時parent的平衡因子+1?

cur插入之后parent的平衡因子可能會有五種可能‘-1’,‘+1’,‘0’,‘-2’,‘+2’

  • ?如果cur插入后parent的平衡因子是0,說明插入之前它的平衡因子是正負1,所以插入后才可能被調整為0,此時cur成功插入
  • 如果cur插入后parent的平衡因子是正負1,說明插入之前它的平衡因子是0,所以插入后才可能被調整為正負1,此時以parent為根的樹高度增加了,上面節點的平衡因子可能會被打破所以需要繼續向上更新
  • 如果cur插入后parent的平衡因子是正負2,則說明以parent為根的樹不符合AVL樹的性質,需要對其進行調整,既旋轉

插入節點

TreeNode node = new TreeNode(val);//如果根節點為空直接插入if (root == null) {root = node;return true;}TreeNode parent = null;TreeNode cur = root;while (cur != null) {if (val > cur.val) {parent = cur;cur = cur.right;} else if (val < cur.val) {parent = cur;cur = cur.left;} else {return false;}}//cur == nullif (val > parent.val) {parent.right = node;} else if (val < parent.val) {parent.left = node;}

調整parent的平衡因子

//調整cur = node;node.parent = parent;while (parent != null) {//如果cur是父節點的右孩子,父節點的平衡因子就加一,否則減一if (cur == parent.right) {parent.bf++;} else {parent.bf--;}//判斷平衡因子if (parent.bf == 0) {//已經平衡break;} else if (parent.bf == 1 || parent.bf == -1) {//繼續向上調整cur = parent;parent = cur.parent;} else {if (parent.bf == 2) {//右樹高,需要降低右樹高度(說明cur插到了parent的右邊)if (cur.bf == 1) {//左單旋} else if (cur.bf == -1) {//右左雙旋}//左樹高,需要降低左樹高度} else {//parent.bf == -2(說明cur插到了parent的左邊)if (cur.bf == -1) {//右單旋} else if (cur.bf == 1) {//左右雙旋}}break;}}

AVL樹的旋轉

右單旋

當新節點插入較高左子樹的左側

上圖是一個簡單的右旋,當新節點10插入到30的左子樹時(這里是左子樹),破壞了avl樹的平衡節點50的平衡因子變成-2,表示左子樹比右子樹高2,所以我們需要降低左子樹,提高右子樹。既將50節點向右旋轉到30節點的左邊(因為50比三十大,只能在30的右邊),此時這顆AVL樹就平衡了(別忘了修改平衡因子bf)

但是實際插入中的AVL樹的結構可能不會和上圖這樣簡單比如:

  • 30的右邊可能已經有節點了

0是新插入的節點插到了30的左子樹的左側,破壞了AVL樹的平衡,使50節點的平衡因子變成-2,所以和剛剛一樣我們需要降低左樹,抬高右樹,我們讓50節點向右旋轉,但是此時30節點的右邊已經有了一個節點(也可能是30的右子樹的根),不過這個節點一定是大于30小于50的,所以我們只需要將這個節點放到50節點的左邊,然后再將50節點放到30節點的右邊即可,最后修改平衡因子。

  • ?50可能是其他節點的左右子樹或者根節點

?如果50是根節點那么當旋轉完成后要更新根節點,如果50是其父節點的左孩子,那么要讓30節點變成50原父節點的左孩子,右孩子則相反

代碼

public void rotateRight(TreeNode parent) {TreeNode pParetn = parent.parent;//記錄parent的父節點TreeNode subL = parent.left;//parent的左孩子TreeNode subLR = subL.right;//parent的左孩子的右孩子parent.left = subLR;if (subLR != null) {subLR.parent = parent;}subL.right = parent;parent.parent = subL;//檢查當前parent是不是根節點//是根節點的話直接讓subL為根節點if (parent == root) {root = subL;root.parent = null;} else {//不是根節點,就判斷parent是其父節點的左孩子還是右孩子if (parent == pParetn.left) {//parent是左孩子,就讓subL也是左孩子pParetn.left = subL;} else {pParetn.right = subL;}subL.parent = pParetn;}subL.bf = 0;parent.bf = 0;}

首先先標記右旋所需要的節點,然后開始右旋,過程和前文一樣

  1. 將subLR作為parent的左孩子(就算subLR為空也無所謂)
  2. 判斷subL是否有右孩子(subLR) ,有的話就令subLR的父節點位parent
  3. 讓subL的右節點為parent
  4. 讓parent的父節點為subLR
  5. 旋轉完成后判斷parent是否是根節點是的話,更新根節點,有父節點的話則讓subL為parent父節點的孩子
  6. 最后修改平衡因子

右旋代碼既圖解?

左單旋

當新節點插入較高右子樹的右側

左單旋的方法和右單旋一樣,只是方向不一樣,具體過程參考右單旋

代碼及圖解

private void rotateLeft(TreeNode parent) {TreeNode subR = parent.right;TreeNode subRL = subR.left;parent.right = subRL;subR.left = parent;if (subRL != null) {subRL.parent = parent;}TreeNode pParent = parent.parent;parent.parent = subR;//檢查當前parent是不是rootif (parent == root) {root = subR;root.parent = null;} else {//不是rootif (pParent.right == parent) {pParent.right = subR;} else {pParent.left = subR;}subR.parent = pParent;}parent.bf = 0;subR.bf = 0;}

?左旋

  1. 首先先標記左旋所需要的節點,然后開始左旋
  2. 將subRL作為parent的右孩子
  3. 判斷subR的左孩子是否為空,如果不為空則令subR的左孩子(subRL)的父節點為parent
  4. 令subR的左孩子為parent
  5. 令parent的parent為subR
  6. 旋轉完成后判斷parent是否是根節點是的話,更新根節點,有父節點的話則讓subR為parent父節點的孩子
  7. 最后修改平衡因子

左右雙旋

新節點插入較高左子樹右側

這種情況無法通過直接左旋或者右旋使樹平衡,需要先左旋再右旋,通過左旋把樹變成左子樹較高的情況然后再右旋

可以看到只是右旋達不到平衡的結果

先左旋再右旋

private void rotateLR(TreeNode parent) {TreeNode subL = parent.left;TreeNode subLR = subL.right;int bf = subLR.bf;rotateLeft(parent.left);rotateRight(parent);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;}}

?以上面的例子說明當bf==1時說明新節點插到45左邊,bf==-1時說明新節點插到45右邊

bf==-1時旋轉結果

右左雙旋

新節點插到較高右子樹左側

private void rotateRL(TreeNode parent) {TreeNode subR = parent.right;TreeNode subRL = subR.left;int bf = subRL.bf;rotateRight(parent.right);rotateLeft(parent);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;}}

總結?

當新節點插入后如果樹不平衡,既parent的平衡因子為-2或者2

1.parent的平衡因子為2,說明右樹高

  • 如果SubR(右子樹)的平衡因子為1,說明新節點插入較高右子樹的右側,執行左單旋
  • 如果SubR的平衡因子為-1,說明新節點插入到較高右子樹的左側,執行右左雙旋

2.parent的平衡因子為-2,說明左樹高

  • 如果SubL的平衡因子為-1,說明新節點插入到了較高左子樹的左邊,執行有右選
  • 如果SubL的平衡因子為1,說明新節點插入到了較高左子樹的右邊,執行左右雙旋?

驗證AVL樹

驗證AVL樹只需要判斷每個節點的左右子樹高度差的絕對值都小于一就行

public int height(TreeNode node) {if (node == null) {return 0;}int left = height(node.left);int right = height(node.right);return left > right ? left + 1 : right + 1;}public Boolean isbalanced(TreeNode root) {if (root == null) {return true;}//求左右子樹高度int left = height(root.left);int right = height(root.right);if (root.bf != right - left) {return false;}return Math.abs(left - right) <= 1&& isbalanced(root.left)&& isbalanced(root.right);}public static void main(String[] args) {int[] array = {17, 4, 8, 6, 17, 25, 17, 19, 10};AVLTree avlTree = new AVLTree();for (int i=0;i<array.length;++i){avlTree.insert(array[i]);}boolean ret = avlTree.isbalanced(avlTree.root);System.out.println(ret);}

綜上,AVL樹其實就是一個相對平衡的二叉搜索樹,每個節點的左右子樹高度差的絕對值都小于一,這樣可以使其查找的時間復雜度穩定為O(logN),但是也因為其在進行修改操作時比如插入刪除,需要對樹進行多次旋轉,使其修改變得及其麻煩。所以,如果需要 一種查詢高效且有序的數據結構,而且數據的個數為靜態的(修改操作較少或不修改),可以考慮AVL樹,但一個結構經常修 改,就不太適合

以上就是博主對AVL樹知識的分享,在之后的博客中會陸續分享有關數據結構的其他知識,如果有不懂的或者有其他見解的歡迎在下方評論或者私信博主,也希望可以多多支持博主!!🥰🥰

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

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

相關文章

泰迪杯特等獎案例學習資料:基于多模態融合與邊緣計算的智能溫室環境調控系統

(第十二屆泰迪杯數據挖掘挑戰賽特等獎案例解析) 一、案例背景與核心挑戰 1.1 應用場景與行業痛點 在現代設施農業中,溫室環境調控直接影響作物產量與品質。傳統溫室管理存在以下問題: 環境參數耦合性高:溫度、濕度、光照、CO?濃度等參數相互影響,人工調控易顧此失彼。…

動手學深度學習12.1. 編譯器和解釋器-筆記練習(PyTorch)

以下內容為結合李沐老師的課程和教材補充的學習筆記&#xff0c;以及對課后練習的一些思考&#xff0c;自留回顧&#xff0c;也供同學之人交流參考。 本節課程地址&#xff1a;無 本節教材地址&#xff1a;12.1. 編譯器和解釋器 — 動手學深度學習 2.0.0 documentation 本節…

[java八股文][Java并發編程面試篇]并發安全

juc包下你常用的類&#xff1f; 線程池相關&#xff1a; ThreadPoolExecutor&#xff1a;最核心的線程池類&#xff0c;用于創建和管理線程池。通過它可以靈活地配置線程池的參數&#xff0c;如核心線程數、最大線程數、任務隊列等&#xff0c;以滿足不同的并發處理需求。Exe…

VMware搭建ubuntu保姆級教程

目錄 VMware Ubuntu 虛擬機配置指南 創建虛擬機 下載 Ubuntu ISO 新建虛擬機 網絡配置&#xff08;雙網卡模式&#xff09; 共享文件夾設置 SSH 遠程訪問配置 VMware Ubuntu 虛擬機配置指南 創建虛擬機 下載 Ubuntu ISO 【可添加我獲取】 官網&#xff1a;Get Ubunt…

馮諾依曼結構與哈佛架構深度解析

一、馮諾依曼結構&#xff08;Von Neumann Architecture&#xff09; 1.1 核心定義 由約翰馮諾依曼提出&#xff0c;程序指令與數據共享同一存儲空間和總線&#xff0c;通過分時復用實現存取。 存儲器總帶寬 指令帶寬 數據帶寬 即&#xff1a;B_mem f_clk W_data f_…

C/C++工程中的Plugin機制設計與Python實現

C/C工程中的Plugin機制設計與Python實現 1. Plugin機制設計概述 在C/C工程中實現Plugin機制通常需要以下幾個關鍵組件&#xff1a; Plugin接口定義&#xff1a;定義統一的接口規范動態加載機制&#xff1a;運行時加載動態庫注冊機制&#xff1a;Plugin向主程序注冊自己通信機…

node-sass安裝失敗解決方案

1、python環境問題 Error: Cant find Python executable "python", you can set the PYTHON env variable. 提示找不到python2.7版本&#xff0c; 方法一&#xff1a;可安裝一個python2.7或引用其他已安裝的python2.7 通過設置環境變量可以解決&#xff1b; 方法二&…

Netty高并發物聯網通信服務器實戰:協議優化與性能調優指南

目錄 1.總體設計 2.自定義協議設計(簡單版) 3.消息類型(1字節) 4.項目結構 5.核心功能代碼 (1)pom.xml(Maven依賴) (2)IotServer.java(服務器啟動器) (3)IotServerInitializer.java(Pipeline初始化) (4)DeviceChannelManager.java(設備連接管理器)…

多模態大語言模型arxiv論文略讀(六十)

Cantor: Inspiring Multimodal Chain-of-Thought of MLLM ?? 論文標題&#xff1a;Cantor: Inspiring Multimodal Chain-of-Thought of MLLM ?? 論文作者&#xff1a;Timin Gao, Peixian Chen, Mengdan Zhang, Chaoyou Fu, Yunhang Shen, Yan Zhang, Shengchuan Zhang, Xi…

面試常問系列(一)-神經網絡參數初始化-之自注意力機制為什么除以根號d而不是2*根號d或者3*根號d

首先先羅列幾個參考文章&#xff0c;大家之后可以去看看&#xff0c;加深理解&#xff1a; 面試常問系列(一)-神經網絡參數初始化面試常問系列(一)-神經網絡參數初始化之自注意力機制_注意力機制的參數初始化怎么做-CSDN博客面試常問系列(一)-神經網絡參數初始化-之-softmax-C…

第5篇:EggJS中間件開發與實戰應用

在Web開發中&#xff0c;中間件&#xff08;Middleware&#xff09;是處理HTTP請求和響應的核心機制之一。EggJS基于Koa的洋蔥模型實現了高效的中間件機制&#xff0c;本文將深入探討中間件的執行原理、開發實踐以及常見問題解決方案。 一、中間件執行機制與洋蔥模型 1. 洋蔥模…

樹狀結構轉換工具類

項目中使用了很多樹狀結構&#xff0c;為了方便使用開發一個通用的工具類。 使用工具類的時候寫一個類基礎BaseNode&#xff0c;如果有個性化字段添加到類里面&#xff0c;然后就可以套用工具類。 工具類會將id和pid做關聯返回一個樹狀結構的集合。 使用了hutool的工具包判空…

【Python】--裝飾器

裝飾器&#xff08;Decorator&#xff09;本質上是一個返回函數的函數 主要作用是&#xff1a;在不修改原函數代碼的前提下&#xff0c;給函數增加額外的功能 比如&#xff1a;增加業務&#xff0c;日志記錄、權限驗證、執行時間統計、緩存等場景 my_decorator def func():pas…

AI教你學VUE——Gemini版

前端開發學習路線圖 (針對編程新手&#xff0c;主攻 Vue 框架) 總原則&#xff1a;先夯實基礎&#xff0c;再深入框架。 想象一下建房子&#xff0c;地基不牢&#xff0c;上面的高樓&#xff08;框架&#xff09;是蓋不起來的。HTML、CSS、JavaScript 就是前端的地基。 階段一…

神經網絡中之多類別分類:從基礎到高級應用

神經網絡中之多類別分類&#xff1a;從基礎到高級應用 摘要 在機器學習領域&#xff0c;多類別分類是解決復雜問題的關鍵技術之一。本文深入探討了神經網絡在多類別分類中的應用&#xff0c;從基礎的二元分類擴展到一對多和一對一分類方法。我們詳細介紹了 softmax 函數的原理…

Go Web 后臺管理系統項目詳解

Go Web 后臺管理系統項目詳解 一、背景介紹 這是一個基于 Go 語言開發的 Web 后臺管理系統&#xff0c;為筆者學習期間練手之作&#xff0c;較為粗糙 二、技術架構 后端 語言 &#xff1a;采用 Go 語言&#xff08;Golang&#xff09;編寫&#xff0c;因其簡潔高效、并發能…

【Python系列】Python 中的 HTTP 請求處理

&#x1f49d;&#x1f49d;&#x1f49d;歡迎來到我的博客&#xff0c;很高興能夠在這里和您見面&#xff01;希望您在這里可以感受到一份輕松愉快的氛圍&#xff0c;不僅可以獲得有趣的內容和知識&#xff0c;也可以暢所欲言、分享您的想法和見解。 推薦:kwan 的首頁,持續學…

OS7.【Linux】基本指令入門(6)

目錄 1.zip和unzip 配置指令 使用 兩個名詞:打包和壓縮 打包 壓縮 Linux下的操作演示 壓縮和解壓縮文件 壓縮和解壓縮目錄 -d選項 2.tar Linux下的打包和壓縮方案簡介 czf選項 xzf選項 -C選項 tzf選項 3.bc 4.uname 不帶選項的uname -a選項 -r選項 -v選項…

windows系統 壓力測試技術

一、CPU壓測模擬 工具&#xff1a;CpuStres v2.0 官網&#xff1a;https://learn.microsoft.com/en-us/sysinternals/downloads/cpustres 功能&#xff1a;是一個工具類&#xff0c;用來模擬在一個進程中啟動最多64個線程&#xff0c;且可以獨立控制任何一個線程的啟動/暫停、…

64.搜索二維矩陣

給你一個滿足下述兩條屬性的 m x n 整數矩陣&#xff1a; 每行中的整數從左到右按非嚴格遞增順序排列。每行的第一個整數大于前一行的最后一個整數。 給你一個整數 target &#xff0c;如果 target 在矩陣中&#xff0c;返回 true &#xff1b;否則&#xff0c;返回 false 。 示…