Java數(shù)據(jù)結(jié)構(gòu)中關(guān)于AVL樹的實現(xiàn)方法詳解
AVL樹的說明
AVL樹是一種自平衡二叉搜索樹,它的名稱來源于它的發(fā)明者 Adelson-Velsky 和 Landis 。在AVL樹中,任何節(jié)點的兩個子樹的高度最多相差 1,這使得AVL樹能夠保持相對平衡,從而保證了樹的查找、插入和刪除操作的時間復(fù)雜度都是 O(log n)。
AVL樹的平衡性是通過對節(jié)點進行旋轉(zhuǎn)操作來實現(xiàn)的,包括左旋、右旋、左右旋和右左旋。當插入或刪除節(jié)點后破壞了AVL樹的平衡性時,就會進行相應(yīng)的旋轉(zhuǎn)操作來保持樹的平衡。
也就是說, AVL 樹是一種特殊的自平衡二叉搜索樹。
AVL樹的成員變量及其構(gòu)造方法
(1)構(gòu)造 AVLNode 內(nèi)部類變量 :
- int key 關(guān)鍵字:通過關(guān)鍵字來比較每個節(jié)點的大小。
- Object value 值:通過該變量存放值。
- AVLNode left:引用左孩子節(jié)點。
- AVLNode right:引用右孩子節(jié)點。
- int height 高度:表示當前節(jié)點的高度,默認初始化為 1 。
(2)AVLNode 內(nèi)部類構(gòu)造方法:
- 重載兩個內(nèi)部類的構(gòu)造方法分別為:參數(shù)為 key,value 的構(gòu)造方法、參數(shù)為 key,value,left,right 的構(gòu)造方法。
(3)構(gòu)造 AVLTree 外部類 :
- AVLNode root:表示該樹的頭節(jié)點。
代碼如下:
public class AVLTree { AVLNode root = null; static class AVLNode { int key; Object value; AVLNode left; AVLNode right; int height = 1; public AVLNode(int key, Object value) { this.key = key; this.value = value; } public AVLNode(int key, Object value, AVLNode left, AVLNode right) { this.key = key; this.value = value; this.left = left; this.right = right; } } }
實現(xiàn)AVL樹的核心方法
AVL 樹的最核心的方法就是插入、更新、刪除操作,因為這些操作都有可能造成二叉搜索樹失去平衡。為了解決自平衡的特點,需要每一個插入或者更新、刪除操作之后,需要檢查是否失去平衡,若失去平衡需要通過左旋、右旋、左右旋、右左旋來重新達到平衡狀態(tài);若沒有失去平衡,無需任何操作。
獲取當前節(jié)點的高度
height(AVLNode node)
不能直接通過 node.height 得到當前節(jié)點的高度,是因為默認高度為 1,若出現(xiàn)該節(jié)點為 null 時,就會出現(xiàn)矛盾,因此需要先判斷該節(jié)點是否為 null 節(jié)點,若為空節(jié)點,返回 0 ;若不為 空節(jié)點,則返回當前節(jié)點 node.height 即可。
代碼如下:
//獲取當前節(jié)點的高度 private int height (AVLNode node) { return node == null ? 0 : node.height; }
更新當前節(jié)點的高度
updateHeight(AVLNode node)
由于通過刪除、插入、旋轉(zhuǎn)都有可能導(dǎo)致當前節(jié)點的高度發(fā)生改變,所以需要更新高度。實現(xiàn)該方法也很簡單,判斷當前節(jié)點的左右節(jié)點的高度,取最大的高度 + 1 就是為當前節(jié)點的高度。
代碼如下:
//更新當前的高度 private void updateHeight (AVLNode node) { node.height = Integer.max(height(node.left),height(node.right)) + 1; }
平衡因子
bf(AVLNode node)
判斷當前節(jié)點是否失去平衡,當該節(jié)點的左子樹的高度 - 右子樹的高度 > 1或者 < -1 即失去平衡了。若差值為 1、0、-1,表示沒有失去平衡。
代碼如下:
//平衡因子 private int bf (AVLNode node) { return height(node.left) - height(node.right); }
對失衡節(jié)點旋轉(zhuǎn)
rotate(AVLNode node)
有四種情況:左旋、右旋、左右旋、右左旋
左旋:需要先拿到失衡節(jié)點 node 的右孩子節(jié)點 node.right ,將 r = node.right 賦值給 r 。先將 r.left 賦值給 node.right ,即 node.right = r.left 進行 "換爹" 操作,然后再 "上位" r.left = node 。最后,因為旋轉(zhuǎn)會導(dǎo)致當前 node 的節(jié)點與上位后的節(jié)點 r 的高度都有可能會改變,所以需要及時更新高度,通過 updateHeight(node),updateHeight(r),需要注意的是,更新的順序不能改變。
右旋:跟左旋的原理是一樣的,需要先拿到失衡節(jié)點 node 的左孩子節(jié)點 node.left ,將 l= node.left賦值給 l。先將 l.right賦值給 node.left,即 node.left= l.right進行 "換爹" 操作,然后再 "上位" l.right= node 。最后,因為旋轉(zhuǎn)會導(dǎo)致當前 node 的節(jié)點與上位后的節(jié)點 r 的高度都有可能會改變,所以需要及時更新高度,通過 updateHeight(node),updateHeight(l),需要注意的是,更新的順序不能改變。
左右旋:通過結(jié)合左旋、右旋實現(xiàn)左右旋。先拿到當前節(jié)點的左節(jié)點 l = node.left,對于 l 節(jié)點需要用到左旋的方法進行旋轉(zhuǎn) leftRotate(l),旋轉(zhuǎn)后需要重新賦值 node.left = leftRotate(l) 。接著對于 node 節(jié)點需用用到右旋方法進行旋轉(zhuǎn) rightRotate(node) 。最后返回rightRotate(node) 節(jié)點即可。
右左旋:通過結(jié)合右旋、左旋實現(xiàn)右左旋。先拿到當前節(jié)點的右節(jié)點 r = node.right,對于 r 節(jié)點需要用到右旋的方法進行旋轉(zhuǎn) rightRotate(r) ,旋轉(zhuǎn)后需要重新賦值 node.right = rightRotate(r) 。接著對于 node 節(jié)點需要用到左旋方法 leftRotate(node) 。最后返回 leftRotate(node) 節(jié)點即可。
代碼如下:
//左旋 private AVLNode leftRotate (AVLNode node) { AVLNode r = node.right; node.right = r.left; r.left = node; updateHeight(node); updateHeight(r); return r; } //右旋 private AVLNode rightRotate (AVLNode node) { AVLNode l = node.left; node.left = l.right; l.right = node; updateHeight(node); updateHeight(l); return l; } //左右旋 private AVLNode leftRightRotate (AVLNode node) { AVLNode l = node.left; node.left = leftRotate(l); return rightRotate(node); } //右左旋 private AVLNode rightLeftRotate (AVLNode node) { AVLNode r = node.right; node.right = rightRotate(r); return leftRotate(node); }
檢查節(jié)點是否平衡與重新平衡
balance(AVLNode node)
介紹四種失衡狀態(tài)的樹
- LL : 當前節(jié)點 node 的左子樹的高度 - 右子樹的高度 > 1,且 node.left 的左子樹的高度 - node.left 的右子樹的高度 >= 0 。實現(xiàn)該情況重新平衡,只需要當前節(jié)點進行右旋操作即可。
- LR:當前節(jié)點 node 的左子樹的高度 - 右子樹的高度 > 1,且 node.left 的左子樹的高度 - node.left 的右子樹的高度 < 0 。實現(xiàn)該情況重新平衡,需要進行先將 node.left 節(jié)點進行左旋,重新 node.left = leftRotate(node.left),接著對于 node 進行右旋即可,也就是上面已經(jīng)實現(xiàn)的左右旋方法。
- RL:當前節(jié)點 node 的左子樹的高度 - 右子樹的高度 < -1 ,且 node.right 的左子樹的高度 - node.right的右子樹的高度 >0 。實現(xiàn)該情況重新平衡,需要用到上面實現(xiàn)了的右左旋方法。
- RR:當前節(jié)點 node 的左子樹的高度 - 右子樹的高度 < -1 ,且 node.right 的左子樹的高度 - node.right 的右子樹的高度 <= 0 。實現(xiàn)該情況重新平衡,只需要左旋一次操作即可。
四種失衡狀態(tài)圖:
代碼如下:
//檢查節(jié)點是否失衡,重新平衡代碼 private AVLNode balance (AVLNode node) { if(node == null) { return null; } if (bf(node) > 1 && bf(node.left) >= 0) { return rightRotate(node); } else if (bf(node) > 1 && bf(node.left) < 0) { return leftRightRotate(node); } else if (bf(node) < -1 && bf(node.right) <= 0) { return leftRotate(node); }else if (bf(node) < -1 && bf(node.right) > 0) { return rightLeftRotate(node); } return node; }
當 node == null 時,返回 null 即可。
插入與更新節(jié)點
put(int key, Object value)
使用遞歸實現(xiàn)插入、更新節(jié)點。兩種情況,若沒有找到 key 關(guān)鍵字時,而找到空位的地方插入新節(jié)點;若找到 key 關(guān)鍵字時,更新該節(jié)點的值即可。區(qū)別于一般的二叉搜索樹,自平衡的二叉搜索樹,需要在插入節(jié)點后更新當前節(jié)點的高度和通過旋轉(zhuǎn)來重新達到平衡。需要注意的是,更新節(jié)點的操作是不會改變高度還有破壞平衡。
代碼如下:
//更新 public AVLNode put (int key, Object value) { return doPut(root,key,value); } private AVLNode doPut(AVLNode node, int key, Object value) { if (node == null) { return new AVLNode(key,value); } if (node.key == key) { node.value = value; return node; } if (node.key > key) { node.left = doPut(node.left,key,value); }else { node.right = doPut(node.right,key,value); } updateHeight(node); return balance(node); }
刪除節(jié)點
remove(AVLNode node)
使用遞歸實現(xiàn)刪除節(jié)點思路:
(1)node == null
(2)沒有找到 key
(3)找到 key 1) 沒有 2)只有一個孩子 3)有兩個孩子
(4)更新高度
(5)balance
代碼如下:
//刪除 public AVLNode remove (int key) { return doRemove(root,key); } private AVLNode doRemove (AVLNode node,int key) { if (node == null) { return null; } if (node.key > key) { node.left = doRemove(node.left,key); } else if (node.key < key) { node.right = doRemove(node.right,key); }else { if (node.left == null && node.right == null) { return null; } else if (node.right == null) { node = node.left; } else if (node.left == null) { node = node.right; }else { AVLNode p = node.right; while (p.left != null) { p = p.left; } p.right = doRemove(node.right,p.key); p.left = node.left; node = p; } } updateHeight(node); return balance(node); }
實現(xiàn)AVLTree核心方法的完整代碼
public class AVLTree { AVLNode root = null; static class AVLNode { int key; Object value; AVLNode left; AVLNode right; int height = 1; public AVLNode(int key, Object value) { this.key = key; this.value = value; } public AVLNode(int key, Object value, AVLNode left, AVLNode right) { this.key = key; this.value = value; this.left = left; this.right = right; } } //獲取當前節(jié)點的高度 private int height (AVLNode node) { return node == null ? 0 : node.height; } //更新當前的高度 private void updateHeight (AVLNode node) { node.height = Integer.max(height(node.left),height(node.right)) + 1; } //平衡因子 private int bf (AVLNode node) { return height(node.left) - height(node.right); } //左旋 private AVLNode leftRotate (AVLNode node) { AVLNode r = node.right; node.right = r.left; r.left = node; updateHeight(node); updateHeight(r); return r; } //右旋 private AVLNode rightRotate (AVLNode node) { AVLNode l = node.left; node.left = l.right; l.right = node; updateHeight(node); updateHeight(l); return l; } //左右旋 private AVLNode leftRightRotate (AVLNode node) { AVLNode l = node.left; node.left = leftRotate(l); return rightRotate(node); } //右左旋 private AVLNode rightLeftRotate (AVLNode node) { AVLNode r = node.right; node.right = rightRotate(r); return leftRotate(node); } //檢查節(jié)點是否失衡,重新平衡代碼 private AVLNode balance (AVLNode node) { if(node == null) { return null; } if (bf(node) > 1 && bf(node.left) >= 0) { return rightRotate(node); } else if (bf(node) > 1 && bf(node.left) < 0) { return leftRightRotate(node); } else if (bf(node) < -1 && bf(node.right) <= 0) { return leftRotate(node); }else if (bf(node) < -1 && bf(node.right) > 0) { return rightLeftRotate(node); } return node; } //更新 public AVLNode put (int key, Object value) { return doPut(root,key,value); } private AVLNode doPut(AVLNode node, int key, Object value) { if (node == null) { return new AVLNode(key,value); } if (node.key == key) { node.value = value; return node; } if (node.key > key) { node.left = doPut(node.left,key,value); }else { node.right = doPut(node.right,key,value); } updateHeight(node); return balance(node); } //刪除 public AVLNode remove (int key) { return doRemove(root,key); } private AVLNode doRemove (AVLNode node,int key) { if (node == null) { return null; } if (node.key > key) { node.left = doRemove(node.left,key); } else if (node.key < key) { node.right = doRemove(node.right,key); }else { if (node.left == null && node.right == null) { return null; } else if (node.right == null) { node = node.left; } else if (node.left == null) { node = node.right; }else { AVLNode p = node.right; while (p.left != null) { p = p.left; } p.right = doRemove(node.right,p.key); p.left = node.left; node = p; } } updateHeight(node); return balance(node); } }
到此這篇關(guān)于Java數(shù)據(jù)結(jié)構(gòu)中關(guān)于AVL樹的實現(xiàn)方法詳解的文章就介紹到這了,更多相關(guān)Java AVL樹內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java數(shù)據(jù)結(jié)構(gòu)之紅黑樹的實現(xiàn)方法和原理詳解
- Java數(shù)據(jù)結(jié)構(gòu)中七種排序算法實現(xiàn)詳解
- Java數(shù)據(jù)結(jié)構(gòu)和算法之鏈表詳解
- Java數(shù)據(jù)結(jié)構(gòu)篇之實現(xiàn)二叉搜索樹的核心方法
- Java數(shù)據(jù)結(jié)構(gòu)與算法之二分查找詳解
- Java數(shù)據(jù)結(jié)構(gòu)中的HashMap和HashSet詳解
- Java常見的數(shù)據(jù)結(jié)構(gòu)之棧和隊列詳解
- java手動實現(xiàn)常見數(shù)據(jù)結(jié)構(gòu)的示例代碼
相關(guān)文章
Spring?BOOT?AOP基礎(chǔ)應(yīng)用教程
這篇文章主要介紹了Spring?BOOT?AOP的使用,文章從相關(guān)問題展開全文內(nèi)容詳情,具有一定的參考價值,需要的小伙伴可以參考一下2022-07-07SpringBoot使用Jasypt對配置文件和數(shù)據(jù)庫密碼加密
在做數(shù)據(jù)庫敏感信息保護時,應(yīng)加密存儲,本文就來介紹一下SpringBoot使用Jasypt對配置文件和數(shù)據(jù)庫密碼加密,具有一定的參考價值,感興趣的可以了解一下2024-02-02Java開發(fā)編程到底是用idea好還是eclipse好
這篇文章主要介紹了Java開發(fā)編程到底是用idea好還是eclipse好,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-08-08Springboot并發(fā)調(diào)優(yōu)之大事務(wù)和長連接
這篇文章主要介紹了Springboot并發(fā)調(diào)優(yōu)之大事務(wù)和長連接,重點分享長事務(wù)以及長連接導(dǎo)致的并發(fā)排查和優(yōu)化思路和示例,具有一定的參考價值,感興趣的可以了解一下2022-05-05SpringCloud使用CircuitBreaker實現(xiàn)熔斷器的詳細步驟
在微服務(wù)架構(gòu)中,服務(wù)之間的依賴調(diào)用非常頻繁,當一個下游服務(wù)因高負載或故障導(dǎo)致響應(yīng)變慢或不可用時,可能會引發(fā)上游服務(wù)的級聯(lián)故障,最終導(dǎo)致整個系統(tǒng)崩潰,熔斷器是解決這類問題的關(guān)鍵模式之一,Spring Cloud提供了對熔斷器的支持,本文將詳細介紹如何集成和使用它2025-02-02使用idea的database模塊繪制數(shù)據(jù)庫er圖的方法
這篇文章主要介紹了使用idea的database模塊繪制數(shù)據(jù)庫er圖,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-07-07