平衡二叉树(AVL)

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. 节点插入与平衡调整

    • 插入新节点时,从根节点开始,根据节点值的大小决定向左子树还是右子树递归插入。
    • 插入完成后,更新节点的高度。
    • 计算平衡因子,如果平衡因子绝对值大于1,说明树失去平衡,需要进行旋转调整:
      • 左左情况(LL):左子树的左子树过高,进行一次右旋。
      • 左右情况(LR):左子树的右子树过高,先对左子树进行左旋,再对根节点进行右旋。
      • 右右情况(RR):右子树的右子树过高,进行一次左旋。
      • 右左情况(RL):右子树的左子树过高,先对右子树进行右旋,再对根节点进行左旋。
  2. 先序遍历

    • 递归遍历树的每个节点,先访问根节点,再递归访问左子树和右子树。
    • 将访问到的节点值依次存入结果数组中。

左右旋是针对一层的操作,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:有时候写代码写迷糊了,就把判断 ==  写成 赋值 =(捂脸)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值