Java數(shù)據(jù)結構中關于AVL樹的實現(xiàn)方法詳解
AVL樹的說明
AVL樹是一種自平衡二叉搜索樹,它的名稱來源于它的發(fā)明者 Adelson-Velsky 和 Landis 。在AVL樹中,任何節(jié)點的兩個子樹的高度最多相差 1,這使得AVL樹能夠保持相對平衡,從而保證了樹的查找、插入和刪除操作的時間復雜度都是 O(log n)。
AVL樹的平衡性是通過對節(jié)點進行旋轉操作來實現(xiàn)的,包括左旋、右旋、左右旋和右左旋。當插入或刪除節(jié)點后破壞了AVL樹的平衡性時,就會進行相應的旋轉操作來保持樹的平衡。
也就是說, AVL 樹是一種特殊的自平衡二叉搜索樹。
AVL樹的成員變量及其構造方法
(1)構造 AVLNode 內部類變量 :
- int key 關鍵字:通過關鍵字來比較每個節(jié)點的大小。
- Object value 值:通過該變量存放值。
- AVLNode left:引用左孩子節(jié)點。
- AVLNode right:引用右孩子節(jié)點。
- int height 高度:表示當前節(jié)點的高度,默認初始化為 1 。
(2)AVLNode 內部類構造方法:
- 重載兩個內部類的構造方法分別為:參數(shù)為 key,value 的構造方法、參數(shù)為 key,value,left,right 的構造方法。
(3)構造 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)
由于通過刪除、插入、旋轉都有可能導致當前節(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é)點旋轉
rotate(AVLNode node)
有四種情況:左旋、右旋、左右旋、右左旋
左旋:需要先拿到失衡節(jié)點 node 的右孩子節(jié)點 node.right ,將 r = node.right 賦值給 r 。先將 r.left 賦值給 node.right ,即 node.right = r.left 進行 "換爹" 操作,然后再 "上位" r.left = node 。最后,因為旋轉會導致當前 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 。最后,因為旋轉會導致當前 node 的節(jié)點與上位后的節(jié)點 r 的高度都有可能會改變,所以需要及時更新高度,通過 updateHeight(node),updateHeight(l),需要注意的是,更新的順序不能改變。
左右旋:通過結合左旋、右旋實現(xiàn)左右旋。先拿到當前節(jié)點的左節(jié)點 l = node.left,對于 l 節(jié)點需要用到左旋的方法進行旋轉 leftRotate(l),旋轉后需要重新賦值 node.left = leftRotate(l) 。接著對于 node 節(jié)點需用用到右旋方法進行旋轉 rightRotate(node) 。最后返回rightRotate(node) 節(jié)點即可。
右左旋:通過結合右旋、左旋實現(xiàn)右左旋。先拿到當前節(jié)點的右節(jié)點 r = node.right,對于 r 節(jié)點需要用到右旋的方法進行旋轉 rightRotate(r) ,旋轉后需要重新賦值 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 進行右旋即可,也就是上面已經實現(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 關鍵字時,而找到空位的地方插入新節(jié)點;若找到 key 關鍵字時,更新該節(jié)點的值即可。區(qū)別于一般的二叉搜索樹,自平衡的二叉搜索樹,需要在插入節(jié)點后更新當前節(jié)點的高度和通過旋轉來重新達到平衡。需要注意的是,更新節(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);
}
}到此這篇關于Java數(shù)據(jù)結構中關于AVL樹的實現(xiàn)方法詳解的文章就介紹到這了,更多相關Java AVL樹內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
SpringBoot中Druid連接池與多數(shù)據(jù)源切換的方法
微服務架構中多數(shù)據(jù)源切換是個常見的需求,Spring Boot 提供了強大的支持來簡化這一過程.本文給大家介紹了SpringBoot中Druid連接池與多數(shù)據(jù)源切換的方法,需要的朋友可以參考下2024-11-11
Spring Security Oauth2.0認證授權教程
Spring Security實現(xiàn)用戶認證、會話管理及授權,支持Token等多方式,OAuth2.0用于分布式系統(tǒng)統(tǒng)一認證,網關解析令牌并轉發(fā)請求2025-07-07
Java數(shù)據(jù)結構之最小堆和最大堆的原理及實現(xiàn)詳解
在計算機科學中,堆(heap)?的實現(xiàn)是一種基于樹的特殊的數(shù)據(jù)結構,它可以在數(shù)組上構建出樹的結構體,并滿足堆的屬性。本文就來和大家詳細聊聊Java數(shù)據(jù)結構中的堆,感興趣的可以了解一下2022-09-09
通過Spring Boot + Mybatis + Redis快速搭建現(xiàn)代化Web項目
本篇文章介紹了如何通過Spring Boot、Mybatis以及Redis快速搭建一個現(xiàn)代化的Web項目,并且同時介紹了如何在Spring Boot下優(yōu)雅地書寫單元測試來保證我們的代碼質量。具體內容詳情大家通過本文學習下吧2017-12-12
如何使用mybatis-plus實現(xiàn)分頁查詢功能
最近在研究mybatis,然后就去找簡化mybatis開發(fā)的工具,發(fā)現(xiàn)就有通用Mapper和mybatis-plus兩個比較好的可是使用,可是經過對比發(fā)現(xiàn)還是mybatis-plus比較好,下面這篇文章主要給大家介紹了關于如何使用mybatis-plus實現(xiàn)分頁查詢功能的相關資料,需要的朋友可以參考下2022-06-06
Spring ApplicationContext接口功能詳細介紹
ApplicationContext是Spring應用程序中的中央接口,由于繼承了多個組件,使得ApplicationContext擁有了許多Spring的核心功能,如獲取bean組件,注冊監(jiān)聽事件,加載資源文件等2023-02-02

