概述
平衡二叉搜索树,实际上就是由于二叉搜索树的左右节点的层数相差一层,导致遍历查找的时候的时间复杂度左右两侧不平衡导致的.
所以可以使用旋转法:如果一个节点的左右孩子,高度差超过1,则此节点失衡,故而需要旋转

由上可以看出左右节点失衡,故而旋转成为下图

新增或者删除的时候可能会导致不平衡,故而此时就需要判断是否需要旋转了
本质就是:如果二叉搜索树的左右节点,高度差超过1的时候,此节点失衡,此时需要通过旋转的方式将二叉搜索树变为平衡二叉搜索树
AVL树
- 二叉搜索树在插入和删除时,节点可能会失去平衡
- 如果在插入和删除时通过旋转,始终让二叉搜索树保持平衡,称为自平衡的二叉搜索树
- AVL是自平衡二叉搜索树的实现之一
平衡二叉搜索树的节点类
//平衡二叉搜索树的节点类
static class AVLNode{
int key; //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;
}
}
求节点的高度
/**
* 求节点高度
* @param node
* @return
*/
private int height(AVLNode node){
//用来判断传过来的节点是否为null,如果为null的话此时的高度返回0,否则返回节点高度
return node == null ? 0 : node.height;
}
更新节点高度
/**
* 更新节点高度(新增,删除,旋转)
* @param node
*/
private void updateHeight(AVLNode node){
//节点的高度就等于左右节点找一个深度更深的节点,让其加1就是此时节点的新高度了
node.height = Integer.max(height(node.left),height(node.right))+1;
}
判断一个节点是否平衡
/**
* 平衡因子(balance factor) = 左子树高度 - 右子树高度
* @param node
* @return
*/
private int bf(AVLNode node){
//如果结果为[-1,1]之间的话,平衡,反之不平衡
return height(node.left)-height(node.right);
}

四种失衡条件
条件1

此时的失衡节点是5,失衡节点的左孩子是3,失衡节点的左孩子的节点左边更高

条件2

此时失衡节点就是6,失衡节点的左孩子是2,时候节点的左右不平衡右边更高一些

条件3

此时失衡节点是2,失衡节点的右节点是6,此时失衡节点的右孩子的左边比右边更大

条件4

失衡节点是2,失衡节点的右孩子是4,失衡节点的右孩子的节点右边比左边高

由上图四种情况可以得知
条件1:可以通过右旋进行改变失衡
条件2:可以先通过左旋,再通过右旋来改变失衡
条件3:可以先通过右旋,再通过左旋来改变失衡
条件4:可以通过左旋进行改变失衡
旋转
左旋----解决条件四

/**
* 通过右旋实现
* @param red 需要旋转的节点
* @return yellow 返回旋转过的节点
*/
private AVLNode rightRotate(AVLNode red){
AVLNode yellow = red.left; //找到红色节点
AVLNode green = yellow.right; //找到绿色节点
yellow.right = red; //将黄色节点的右侧变为红色节点
red.left = green; //将红色节点的左侧变为绿色节点
updateHeight(red); //更新红色节点的高度
updateHeight(yellow); //更新黄色节点的高度
return yellow; //最终返回旋转之后的黄色节点
}

右旋----解决条件一

/**
* 通过左旋实现
* @param red 需要旋转的节点
* @return yellow 返回旋转过的节点
*/
private AVLNode leftRotate(AVLNode red){
AVLNode yellow = red.right; //找到黄色节点
AVLNode green = yellow.left; //找到绿色节点
yellow.left = red; //黄色节点的左侧换位红色节点
red.right = green; //红色节点的右侧换位绿色节点
updateHeight(red); //更新红色节点的高度
updateHeight(yellow); //更新黄色节点的高度
return yellow;
}
}

先左旋再右旋---解决条件二

node.left = leftRotate(node.left); //根节点的左孩子等于旋转之后


rightRotate(node);

/**
* 先左旋左子树,再右旋根节点
* @param node
* @return
*/
private AVLNode leftRightRotate(AVLNode node){
node.left = leftRotate(node.left); //根节点的左孩子等于旋转之后
return rightRotate(node); //再次右旋根节点
}
先右旋再左旋---解决条件三
/**
* 先右旋右子树,再左旋根节点
* @param node
* @return
*/
private AVLNode rightLeftRotate(AVLNode node){
node.right = rightRotate(node.right); //先右旋右子树
return leftRotate(node); //再次左旋根节点
}
失衡解决

/**
* 检查节点是否失衡,重新平衡代码
* @param node
* @return
*/
private AVLNode balance(AVLNode node){
if(node == null){
return null;
}
int bf = bf(node);
if(bf > 1 && bf(node.left) >= 0){ //条件1
return rightRotate(node);
}else if(bf > 1 && bf(node.left) < 0){ //条件3
return leftRightRotate(node);
}else if(bf < -1 && bf(node.right) > 0){ //条件4
return rightLeftRotate(node);
}else if(bf < -1 && bf(node.right) <= 0 ){ //条件2
return leftRotate(node);
}
return node;
}
新增
AVLNode root;
public void put(int key,Object value){
root = dePut1(root,key,value);
}
/**
* 非递归
* @param node
* @param key
* @param value
* @return
*/
private AVLNode dePut(AVLNode node,int key,Object value){
//找到空位创建新节点
//key在树中已经存在,更新
//继续查找
// 将当前节点初始化为AVL树的根节点
AVLNode curr = node;
// 将父节点初始化为null,用于跟踪当前节点的父节点
AVLNode parent = null;
// 遍历AVL树,直到当前节点为null
while (curr != null){
// 如果给定的键小于当前节点的键
if(key < curr.key){
// 更新父节点并移动到左子节点
parent = curr;
curr = curr.left;
}
// 如果给定的键大于当前节点的键
else if(key > curr.key){
// 更新父节点并移动到右子节点
parent = curr;
curr = curr.right;
}
// 如果给定的键与当前节点的键相匹配
else{
// 更新现有节点的值并返回该节点
curr.value = value;
return curr;
}
}
// 如果树为空(没有根节点),返回null
if(parent == null){
return null;
}
// 根据新节点与其父节点的关系,插入具有给定键和值的新节点
if(key < parent.key){
// 如果键小于父节点的键,则插入为左子节点
parent.left = new AVLNode(key, value);
}
else if(key > parent.key){
// 如果键大于父节点的键,则插入为右子节点
parent.right = new AVLNode(key, value);
}
updateHeight(curr); //更新节点高度
return balance(curr); //重新平衡代码
}
/**
* 递归方式
* @param node
* @param key
* @param value
* @return
*/
private AVLNode dePut1(AVLNode node,int key,Object value){
//找到空位创建新节点
//key在树中已经存在,更新
//继续查找
// 如果当前节点为空,表示在空树中插入新节点,返回新的AVL节点
if(node == null){
return new AVLNode(key, value);
}
// 如果给定的键等于当前节点的键,更新当前节点的值并返回当前节点
if(key == node.key){
node.value = value;
return node;
}
// 如果给定的键小于当前节点的键,递归地在左子树中插入新节点
if(key < node.key){
node.left = dePut1(node.left, key, value);
}
// 如果给定的键大于当前节点的键,递归地在右子树中插入新节点
else{
node.right = dePut1(node.right, key, value);
}
updateHeight(node); //更新节点的高度
return balance(node); //重新平衡
}
删除
private AVLNode doRemove(AVLNode node,int key){
//1.node == null
if(node == null){
return null;
}
//2.没找到key
if(key < node.key){
node.left = doRemove(node.left,key);
}else if(key > node.key){
node.right = doRemove(node.right,key);
}else{
//3.找到key
//左右孩子都为空
if(node.left == null && node.right == null){
return null;
}
//判断只有右孩子
else if(node.left == null){
node = node.right;
}
//判断只有左孩子
else if(node.right == null){
node = node.left;
}
//判断左右孩子都存在
else{
AVLNode s = node.right;
while (s.left != null){
s = s.left;
}
//找到后继节点
s.right = doRemove(node.right,s.key);
s.left = node.left;
node = s;
}
}
//4.更新高度
updateHeight(node);
//5.检查失衡
return balance(node);
}
总代码
public class AVLTree {
static class AVLNode{
int key; //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;
}
}
/**
* 求节点高度
* @param node
* @return
*/
private int height(AVLNode node){
return node == null ? 0 : node.height;
}
/**
* 更新节点高度(新增,删除,旋转)
* @param node
*/
private void updateHeight(AVLNode node){
node.height = Integer.max(height(node.left),height(node.right))+1;
}
/**
* 平衡因子(balance factor) = 左子树高度 - 右子树高度
* @param node
* @return
*/
private int bf(AVLNode node){
return height(node.left)-height(node.right);
}
/**
* 通过右旋实现
* @param red 需要旋转的节点
* @return yellow 返回旋转过的节点
*/
private AVLNode rightRotate(AVLNode red){
AVLNode yellow = red.left; //找到旋转的节点
AVLNode green = yellow.right; //找到旋转节点的右节点
yellow.right = red; //旋转之后的右节点是旋转之前的节点
red.left = green; //旋转子厚的右节点的左节点是旋转节点的右节点
updateHeight(red); //更新红色节点的高度
updateHeight(yellow); //更新黄色节点的高度
return yellow;
}
/**
* 通过左旋实现
* @param red 需要旋转的节点
* @return yellow 返回旋转过的节点
*/
private AVLNode leftRotate(AVLNode red){
AVLNode yellow = red.right; //找到黄色节点
AVLNode green = yellow.left; //找到绿色节点
yellow.left = red; //黄色节点的左侧换位红色节点
red.right = green; //红色节点的右侧换位绿色节点
updateHeight(red); //更新红色节点的高度
updateHeight(yellow); //更新黄色节点的高度
return yellow;
}
/**
* 先左旋左子树,再右旋根节点
* @param node
* @return
*/
private AVLNode leftRightRotate(AVLNode node){
node.left = leftRotate(node.left); //根节点的左孩子等于旋转之后
return rightRotate(node); //再次右旋根节点
}
/**
* 先右旋右子树,再左旋根节点
* @param node
* @return
*/
private AVLNode rightLeftRotate(AVLNode node){
node.right = rightRotate(node.right); //先右旋右子树
return leftRotate(node); //再次左旋根节点
}
/**
* 检查节点是否失衡,重新平衡代码
* @param node
* @return
*/
private AVLNode balance(AVLNode node){
if(node == null){
return null;
}
int bf = bf(node);
if(bf > 1 && bf(node.left) >= 0){ //条件1
return rightRotate(node);
}else if(bf > 1 && bf(node.left) < 0){ //条件3
return leftRightRotate(node);
}else if(bf < -1 && bf(node.right) > 0){ //条件4
return rightLeftRotate(node);
}else if(bf < -1 && bf(node.right) <= 0 ){ //条件2
return leftRotate(node);
}
return node;
}
AVLNode root;
public void put(int key,Object value){
root = dePut1(root,key,value);
}
/**
* 非递归
* @param node
* @param key
* @param value
* @return
*/
private AVLNode dePut(AVLNode node,int key,Object value){
//找到空位创建新节点
//key在树中已经存在,更新
//继续查找
// 将当前节点初始化为AVL树的根节点
AVLNode curr = node;
// 将父节点初始化为null,用于跟踪当前节点的父节点
AVLNode parent = null;
// 遍历AVL树,直到当前节点为null
while (curr != null){
// 如果给定的键小于当前节点的键
if(key < curr.key){
// 更新父节点并移动到左子节点
parent = curr;
curr = curr.left;
}
// 如果给定的键大于当前节点的键
else if(key > curr.key){
// 更新父节点并移动到右子节点
parent = curr;
curr = curr.right;
}
// 如果给定的键与当前节点的键相匹配
else{
// 更新现有节点的值并返回该节点
curr.value = value;
return curr;
}
}
// 如果树为空(没有根节点),返回null
if(parent == null){
return null;
}
// 根据新节点与其父节点的关系,插入具有给定键和值的新节点
if(key < parent.key){
// 如果键小于父节点的键,则插入为左子节点
parent.left = new AVLNode(key, value);
}
else if(key > parent.key){
// 如果键大于父节点的键,则插入为右子节点
parent.right = new AVLNode(key, value);
}
updateHeight(curr); //更新节点高度
return balance(curr); //重新平衡代码
}
/**
* 递归方式
* @param node
* @param key
* @param value
* @return
*/
private AVLNode dePut1(AVLNode node,int key,Object value){
//找到空位创建新节点
//key在树中已经存在,更新
//继续查找
// 如果当前节点为空,表示在空树中插入新节点,返回新的AVL节点
if(node == null){
return new AVLNode(key, value);
}
// 如果给定的键等于当前节点的键,更新当前节点的值并返回当前节点
if(key == node.key){
node.value = value;
return node;
}
// 如果给定的键小于当前节点的键,递归地在左子树中插入新节点
if(key < node.key){
node.left = dePut1(node.left, key, value);
}
// 如果给定的键大于当前节点的键,递归地在右子树中插入新节点
else{
node.right = dePut1(node.right, key, value);
}
updateHeight(node); //更新节点的高度
return balance(node); //重新平衡
}
public void remove(int key){
root = doRemove(root,key);
}
private AVLNode doRemove(AVLNode node,int key){
//1.node == null
if(node == null){
return null;
}
//2.没找到key
if(key < node.key){
node.left = doRemove(node.left,key);
}else if(key > node.key){
node.right = doRemove(node.right,key);
}else{
//3.找到key
//左右孩子都为空
if(node.left == null && node.right == null){
return null;
}
//判断只有右孩子
else if(node.left == null){
node = node.right;
}
//判断只有左孩子
else if(node.right == null){
node = node.left;
}
//判断左右孩子都存在
else{
AVLNode s = node.right;
while (s.left != null){
s = s.left;
}
//找到后继节点
s.right = doRemove(node.right,s.key);
s.left = node.left;
node = s;
}
}
//4.更新高度
updateHeight(node);
//5.检查失衡
return balance(node);
}
}
503

被折叠的 条评论
为什么被折叠?



