AVL的相关定义
(摘自胡凡老师的《算法笔记》P391)
AVL树的插入问题
这关乎到AVL的建立,参考Q3
左旋右旋
平衡调整
LL型
LR型&RR型
RL型
1.二叉查找树的平衡因子
我们需要构建一棵二叉查找树(BST),然后通过中序遍历获取每个节点的平衡因子。平衡因子定义为节点的左子树高度与右子树高度之差
#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
const int MAXN = 50;
struct Node
{
int data;
int height;
int l, r;
} nodes[MAXN];
int nodeCount = 0;
int newNode(int data)
{
nodes[nodeCount].data = data;
nodes[nodeCount].height = 1;
nodes[nodeCount].l = nodes[nodeCount].r = -1;
return nodeCount++;
}
int getHeight(int root)
{
if (root == -1)
{
return 0;
}
else
{
return nodes[root].height;
}
}
void updateHeight(int root)
{
nodes[root].height = max(getHeight(nodes[root].l), getHeight(nodes[root].r)) + 1;
}
int getBalanceFactor(int root)
{
return getHeight(nodes[root].l) - getHeight(nodes[root].r);
}
int insert(int root, int data)
{
if (root == -1)
{
return newNode(data);
}
if (data < nodes[root].data)
{
nodes[root].l = insert(nodes[root].l, data);
}
else
{
nodes[root].r = insert(nodes[root].r, data);
}
updateHeight(root);
return root;
}
vector<int> balanceFactor;
void inOrder(int root)
{
if (root == -1)
{
return;
}
inOrder(nodes[root].l);
balanceFactor.push_back(getBalanceFactor(root));
inOrder(nodes[root].r);
}
int main()
{
int n, data, root = -1;
scanf("%d", &n);
for (int i = 0; i < n; i++)
{
scanf("%d", &data);
root = insert(root, data);
}
inOrder(root);
for (int i = 0; i < (int)balanceFactor.size(); i++)
{
printf("%d", balanceFactor[i]);
if (i < (int)balanceFactor.size() - 1)
{
printf(" ");
}
}
return 0;
}
2.平衡二叉树的判定
这道题的亮点是 递归判断每个子树是否满足AVL树的条件。
#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
const int MAXN = 50;
struct Node
{
int data;
int height;
int l, r;
} nodes[MAXN];
int nodeCount = 0;
int newNode(int data)
{
nodes[nodeCount].data = data;
nodes[nodeCount].height = 1;
nodes[nodeCount].l = nodes[nodeCount].r = -1;
return nodeCount++;
}
int getHeight(int root)
{
if (root == -1)
{
return 0;
}
else
{
return nodes[root].height;
}
}
void updateHeight(int root)
{
nodes[root].height = max(getHeight(nodes[root].l), getHeight(nodes[root].r)) + 1;
}
int getBalanceFactor(int root)
{
return getHeight(nodes[root].l) - getHeight(nodes[root].r);
}
int insert(int root, int data)
{
if (root == -1)
{
return newNode(data);
}
if (data < nodes[root].data)
{
nodes[root].l = insert(nodes[root].l, data);
}
else
{
nodes[root].r = insert(nodes[root].r, data);
}
updateHeight(root);
return root;
}
//从根节点开始,递归判断每个子树是否满足AVL树的条件
bool isAVL(int root)
{
if (root == -1)
{
return true;
}
return isAVL(nodes[root].l) && isAVL(nodes[root].r) && abs(getBalanceFactor(root)) <= 1;
}
int main()
{
int n, data, root = -1;
scanf("%d", &n);
for (int i = 0; i < n; i++)
{
scanf("%d", &data);
root = insert(root, data);
}
printf(isAVL(root) ? "Yes" : "No");
return 0;
}
3.平衡二叉树的建立
AVL树是一种自平衡二叉搜索树,其特点是任何节点的两个子树的高度差最多为1。在插入新节点时,AVL树会通过旋转操作来保持平衡。
本题在一边插入节点,一边进行平衡调整。平衡调整可参考本文开始引用胡凡的《算法笔记》部分
具体过程细节
-
节点插入与平衡调整:
- 插入新节点时,从根节点开始,根据节点值的大小决定向左子树还是右子树递归插入。
- 插入完成后,更新节点的高度。
- 计算平衡因子,如果平衡因子绝对值大于1,说明树失去平衡,需要进行旋转调整:
- 左左情况(LL):左子树的左子树过高,进行一次右旋。
- 左右情况(LR):左子树的右子树过高,先对左子树进行左旋,再对根节点进行右旋。
- 右右情况(RR):右子树的右子树过高,进行一次左旋。
- 右左情况(RL):右子树的左子树过高,先对右子树进行右旋,再对根节点进行左旋。
-
先序遍历:
- 递归遍历树的每个节点,先访问根节点,再递归访问左子树和右子树。
- 将访问到的节点值依次存入结果数组中。
左右旋是针对一层的操作,getHeightBalance函数则是针对两层的判断
关于左旋:
左旋是用于解决右重的情况,即当前节点的右子树比左子树高。左旋的目标是将当前节点的右子树提升到当前节点的位置,同时将当前节点下移到新的右子树的位置。核心代码如下:
int L(int root) //左旋:用于解决右子树过高
{
//1.找到旋转节点的右子节点:
int temp = nodes[root].r;
//2.调整子树结构://Step1 将 temp 节点的左子树(即 nodes[temp].l)挂到 root 节点的右子树位置
nodes[root].r = nodes[temp].l;
//Step2 将 root 节点挂到 temp 节点的左子树位置
nodes[temp].l = root;//更新节点高度:
updateHeight(root);
updateHeight(temp);//Step3 将根节点设置为temp
return temp;
}
关于右旋:
int R(int root) //右旋:用于解决左子树过高
{
int temp = nodes[root].l;
nodes[root].l = nodes[temp].r;
nodes[temp].r = root;
updateHeight(root);
updateHeight(temp);
return temp;
}
以下是插入5、3、2三个节点的可视化过程:
代码如下:
#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
const int MAXN = 50;
struct Node
{
int data;
int height;
int l, r;
} nodes[MAXN];
int nodeCount = 0;
int newNode(int data)
{
nodes[nodeCount].data = data;
nodes[nodeCount].height = 1;
nodes[nodeCount].l = nodes[nodeCount].r = -1;
return nodeCount++;
}
int getHeight(int root)
{
if (root == -1)
{
return 0;
}
else
{
return nodes[root].height;
}
}
void updateHeight(int root)
{
nodes[root].height = max(getHeight(nodes[root].l), getHeight(nodes[root].r)) + 1;
}
int getBalanceFactor(int root)
{
return getHeight(nodes[root].l) - getHeight(nodes[root].r);
}
int L(int root) //左旋:用于解决右子树过高
{
//1.找到旋转节点的右子节点:
int temp = nodes[root].r;
//2.调整子树结构:
//Step1 将 temp 节点的左子树(即 nodes[temp].l)挂到 root 节点的右子树位置
nodes[root].r = nodes[temp].l;
//Step2 将 root 节点挂到 temp 节点的左子树位置
nodes[temp].l = root;
//更新节点高度:
updateHeight(root);
updateHeight(temp);
//Step3 将根节点设置为temp
return temp;
}
int R(int root) //右旋:用于解决左子树过高
{
int temp = nodes[root].l;
nodes[root].l = nodes[temp].r;
nodes[temp].r = root;
updateHeight(root);
updateHeight(temp);
return temp;
}
int insert(int root, int data)
{
if (root == -1)
{
return newNode(data);
}
if (data < nodes[root].data)
{
nodes[root].l = insert(nodes[root].l, data);
updateHeight(root);
if (getBalanceFactor(root) == 2)//左子树高
{
if (getBalanceFactor(nodes[root].l) == 1) //LL型
{
root = R(root);//右旋
}
else if (getBalanceFactor(nodes[root].l) == -1) //LR型
{
nodes[root].l = L(nodes[root].l);//先变成LL型
root = R(root);//再右旋
}
}
}
else
{
nodes[root].r = insert(nodes[root].r, data);
updateHeight(root);
if (getBalanceFactor(root) == -2)//右子树高
{
if (getBalanceFactor(nodes[root].r) == -1) //RR型
{
root = L(root);//左旋
}
else if (getBalanceFactor(nodes[root].r) == 1) //RL型
{
nodes[root].r = R(nodes[root].r);//先右旋变成RR型
root = L(root);//再左旋
}
}
}
return root;
}
vector<int> pre;
void preOrder(int root)
{
if (root == -1)
{
return;
}
pre.push_back(nodes[root].data);
preOrder(nodes[root].l);
preOrder(nodes[root].r);
}
int main()
{
int n, data, root = -1;
scanf("%d", &n);
for (int i = 0; i < n; i++)
{
scanf("%d", &data);
root = insert(root, data);
}
preOrder(root);
for (int i = 0; i < (int)pre.size(); i++)
{
printf("%d", pre[i]);
if (i < (int)pre.size() - 1)
{
printf(" ");
}
}
return 0;
}
ps:有时候写代码写迷糊了,就把判断 == 写成 赋值 =(捂脸)