JAVA实现 - AVL树
二叉搜索树的哪些节点需要旋转 ?
如果一个节点的左右孩子,高度差超过1,则此节点失衡,才需要旋转
AVL树:
- 二叉搜索树在插入和删除时,节点可能失衡
- 如果在插入和删除时通过旋转,始终让二叉搜索树保持平衡,称为自自平衡的二叉搜索树
- AVL是自平衡二叉搜索树的实现之一
高度和平衡因子
平衡因子(balance factor) = 左子树的高度 - 右子树的高度
- bf = -1 ,bf = 0,bf = 1 为平衡节点,
- bf>1: 表示左边太高
- bf<-1: 表示右边太高
package com.datastructure.binarytree.avltree;
public class AVLTree {
static class AVLNode{
int key;
Object value;
AVLNode left;
AVLNode right;
int height = 1;
public AVLNode(int key) {
this.key = key;
}
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;
}
}
/**
* 获取AVL树节点的高度
* @param node
* @return
*/
public int height(AVLNode node){
return node == null ? 0:node.height;
}
/**
* 更新节点的高度(新增、删除、旋转)
* @param node
*/
public void updateHeight(AVLNode node){
node.height = Integer.max(height(node.left), height(node.right)) + 1;
}
/**
* 求节点的平衡因子(balance factor) = 左子树的高度 - 右子树的高度
* 性质: -1,0,1 为平衡节点,
* bf>1: 表示左边太高
* bf<-1: 表示右边太高
* @param node
* @return
*/
public int bf(AVLNode node){
return height(node.left) - height(node.right);
}
}
失衡的四种情况
颜色意义:
黄色:上移
红色:下移
绿色:换爹
LL:
- 失衡节点的bf > 1,即左边更高
- 失衡节点的左孩子的bf>=0,即左孩子这边也是左边更高或等高
LR:
- 失衡节点的bf > 1,即左边更高
- 失衡节点的左孩子的bf<0,即左孩子这边是右边更高
RL:
- 失衡节点的bf<-1,即右边更高
- 失衡节点的右孩子的bf>0,即右孩子这边左边更高
RR:
- 失衡节点的bf<-1,即右边更高
- 失衡节点的右孩子的bf<=0,即右孩子这边右边更高或等高
旋转
- 红色和黄色节点旋转后高度可能回发生变化,因此旋转后需要更新高度信息,其他节点旋转后高度不变
- 更新高的顺序不能发生变化,必须先更新位置较低的红色节点的高度,再更新位置较高的黄色节点的高度
package com.datastructure.binarytree.avltree;
public class AVLTree {
...
//右旋
public AVLNode rightRotate(AVLNode red){
AVLNode yellow = red.left;
AVLNode green = yellow.right;
//上位
yellow.right = red;
//绿色换爹
red.left = green;
updateHeight(red); //更新高度
updateHeight(yellow); //更新高度
return yellow;
}
//左旋
public AVLNode leftRotate(AVLNode red){
AVLNode yellow = red.right;
AVLNode green = yellow.left;
//上位
yellow.left = red;
//绿色换爹
red.right = green;
updateHeight(red); //更新高度
updateHeight(yellow); //更新高度
return yellow;
}
//先左子树左旋,再根节点右旋
public AVLNode leftRightNode(AVLNode node){
node.left = leftRotate(node.left); //左子树左旋
return rightRotate(node);
}
//先右子树右旋,再根节点左旋
public AVLNode rightLeftNode(AVLNode node){
node.right = rightRotate(node.right);
return leftRightNode(node);
}
}
平衡
package com.datastructure.binarytree.avltree;
public class AVLTree {
...
public AVLNode balance(AVLNode node){
if(node == null){
return null;
}
int bf = bf(node);
/*
等于0,比较特殊发生再删除时.6节点失衡,6的高度差为0
6
/
3
/ \
1 5
*/
if (bf > 1 && bf(node.left) >= 0){ //左边失衡,LL
return rightRotate(node);
}else if(bf > 1 && bf(node.left) <0 ) { //LR
return leftRightNode(node);
}else if(bf < -1 && bf(node.right) >0 ){ //右边失衡,RL
return rightLeftNode(node);
}else if (bf < -1 && bf(node.right) <= 0){ // RR
return leftRightNode(node);
}
return node;
}
}
更新节点
package com.datastructure.binarytree.avltree;
public class AVLTree {
...
AVLNode root;
public void put(int key, Object value) {
root = doPut(root, key, value);
}
public AVLNode doPut(AVLNode curr, int key, Object value) {
//1.找到空位,返回节点
if (curr == null) {
return new AVLNode(key, value);
}
//2.找到更新
if (key == curr.key) {
//找到
curr.value = value;
return curr;
}
//3.没找到继续找
if (key < curr.key) {
curr.left = doPut(curr.left, key, value);
} else {
curr.right = doPut(curr.right, key, value);
}
//4.更新节点攻读
updateHeight(curr);
//5.平衡节点
return balance(curr);
}
}
删除节点
package com.datastructure.binarytree.avltree;
public class AVLTree {
...
/**
* 根据key删除节点
*
* @param key
* @return
*/
public AVLNode remove(int key) {
root = doRemove(root, key);
return null;
}
/**
* 递归删除方法
*
* @param curr 被删除的树
* @param key 需要删除的key
* @return 删剩下的节点
*/
public AVLNode doRemove(AVLNode curr, int key) {
if (curr == null) {
return null;
}
//1.没找到key
if (key < curr.key) {
curr.left = doRemove(curr.left, key);
} else if (curr.key < key) {
curr.right = doRemove(curr.right, key);
} else {
//2.找打key:1)key为叶子节点 2)只有左孩子 3) 只有右孩子
if (curr.left == null && curr.right == null) { //叶子节点
return null;
} else if (curr.left == null) { // 只有右孩子
curr = curr.right;
} else if (curr.right == null) { //只有左孩子
curr = curr.left;
} else { //既有左孩子也有右孩子,寻找后继节点
AVLNode s = curr.right; //后继节点
while(s.left != null){
s = s.left;
}
//找到后继节点
s.right = doRemove(curr.right,s.key);
s.left = curr.left;
}
}
//更新高度
updateHeight(curr);
//检查平衡
return balance(curr);
}
}
本文来自博客园,作者:chuangzhou,转载请注明原文链接:https://www.cnblogs.com/czzz/p/17939391
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
2022-01-01 UnitTest 框架学习