【数据结构X.10】平衡二叉树的创建,RR旋转,LL旋转,RL旋转,LR旋转,插入、平衡二叉树删除节点和查找插入路径上,距离插入节点最近的平衡因子绝对值大于1的节点,二叉平衡树的高度和平衡因子计算

32 篇文章 0 订阅
16 篇文章 1 订阅

参考:

  1. AVL-平衡二叉树的原理和实现 by 超级小小黑
  2. 王道《数据结构》
  3. 查找——平衡二叉树的实现(代码超详细注释) 前半部分的《大话数据结构》讲得还可以

由于我写博客只是存档用,只描述下实现思路,及给出一下例子验证实现的正确性:

实现难点:

  1. 平衡二叉树只是一种特殊的二叉排序树,其左右子树的高度差小于1。这里讲了二叉排序树的基本操作和知识:
    【数据结构X.9】二叉排序树(BST)的插入、查找(非递归),递归算法、中序遍历二叉排序树,输出有序树,层序遍历BST树、创建一颗二叉排序树、二叉排序树的删除
  2. 左旋、右旋、左旋后右旋、右旋后左旋的理解:实际就是通过左右旋转,使得平衡树始终保持平衡。平衡树神奇的一点就在于,即使整棵树乱七八糟的,只要找到距离插入节点最近的平衡因子为2的祖先节点,然后围绕它进行旋转即可。部分平衡了,这样的平衡就可以维持到整棵树。因为平衡二叉树的平衡因子 B a l a n c e   F a c t o r Balance \ Factor Balance Factor的定义就是 ∣ 左 子 树 高 度 − 右 子 树 高 度 ∣ < 1 |左子树高度-右子树高度|<1 <1所以只要从插入节点开始向上查找,找到失衡节点,然后将其平衡掉就行。
    这样的例子现实生活中也会有:
    【社会中突然出现了捣乱分子,维稳人员进行维稳后,只要稳住这一部分人,又使得社会是和谐社会了 = =】
    只需要处理捣乱的那一部分(平衡因子为2),就可以保证树是平衡的。这是由平衡二叉树的递归性质决定的
  3. 那么如何从插入节点开始向上查找,找到失衡节点呢?由于递归性质,我们知道,从一个根节点,如果能访问到某个叶子节点,那么肯定会经过其一个个祖先节点。所以,只需要在递归的过程中,一边递归,一边记录当前节点的高度,并且计算平衡因子,直到找到平衡因子为2的节点为止。
  4. 如何判断是哪一种类型的旋转:出现了平衡因子为2的节点B,可以根据其平衡因子和左右节点的平衡因子的关系来判断是LL、RR、LR、RR类型。显然平衡因子(Balance Factor)= 左子树高度-右子树高度,那么——
    倘若 B F = − 2 BF=-2 BF=2那么必然有左子树高度比右子树高度小2,新的结点肯定是插入了右子树了,
    ①当节点B的左孩子的平衡因子为1,那么肯定是左边的高度比右边的高度大1,肯定是插入在了右子树的左边,也就是RL类型
    ②当节点B的左孩子的平衡因子为-1,那么肯定是右边的高度比左边的高度大1,肯定是插入在了右子树的右边,也就是RR类型
    以此类推……
  5. 二叉树的删除过程的理解【数据结构X.9】二叉排序树(BST)的插入、查找(非递归),递归算法、中序遍历二叉排序树,输出有序树,层序遍历BST树、创建一颗二叉排序树、二叉排序树的删除
    简单来说,就是: 1. 需 删 除 的 是 叶 子 或 单 孩 子 节 点 , 直 接 用 其 孩 子 ( 叶 子 节 点 的 孩 子 是 N U L L ) 替 换 掉 要 删 除 的 节 点 1.需删除的是叶子或单孩子节点,直接用其孩子(叶子节点的孩子是NULL)替换掉要删除的节点 1.NULL 2. 找 到 右 子 树 最 小 的 元 素 ( 也 就 是 后 继 节 点 ) , 替 代 待 删 除 节 点 2.找到右子树最小的元素(也就是后继节点),替代待删除节点 2.

输入:

例子1:

在这里插入图片描述

5
66
12
87
99
43
123
0
例子2:

在这里插入图片描述

3
7
8
9
10
15
0

实现函数:

#include <iostream>
#include <math.h>
#include <queue>

typedef struct AVLNode
{
    int data;   //数据大小
    int height; //树的深度
    int bf;     //平衡因子
    AVLNode *lchild, *rchild;
} AVLNode, *AVLTree;

/**
 * 层序遍历BST树
 */
void VisitLevelAVLTree(AVLTree tr)
{
    std::cout << "\n层次访问树: " << std::endl;
    std::queue<AVLNode *> q;
    AVLNode *p = tr, *r = tr;
    q.push(p);
    while (!q.empty())
    {
        p = q.front();
        q.pop();
        if (p)
        {
            std::cout << p->data << " height: " << p->height << " bf:" << p->bf << " ";
            if (p->lchild)
                q.push(p->lchild);
            if (p->rchild)
                q.push(p->rchild);
            if (p == r)
            {
                std::cout << std::endl;
                r = q.back();
            }
        }
    }
}

void VisitAVLTree(AVLTree tr)
{
    if (tr)
    {
        VisitAVLTree(tr->lchild);
        std::cout << tr->data << " height: " << tr->height << " bf:" << tr->bf << " \n";
        VisitAVLTree(tr->rchild);
    }
}

/***
 * 获取节点从叶子到该节点的的高度
 */
int GetHeight(AVLNode *tr)
{
    if (tr)
    {
        return tr->height;
    }
    return 0;
}

/***
 * 计算节点的平衡因子
 */
int CalCulateBF(AVLNode *tr)
{
    if (tr)
    {
        return GetHeight(tr->lchild) - GetHeight(tr->rchild);
    }
    return 0;
}

/**
 * 右旋
 */
void R_Rotate(AVLTree &tr)
{
    AVLNode *tmp = tr->lchild;
    tr->lchild = tmp->rchild;
    tmp->rchild = tr;

    //旋转完毕后,更新下高度值
    tr->height = 1 + std::max(GetHeight(tr->lchild), GetHeight(tr->rchild));
    tr->bf = CalCulateBF(tr);
    tmp->height = 1 + std::max(GetHeight(tmp->lchild), GetHeight(tmp->rchild));
    tmp->bf = CalCulateBF(tmp);
    tr = tmp;
}

/**
 * 左旋
 */
void L_Rotate(AVLTree &tr)
{
    AVLNode *tmp = tr->rchild;
    tr->rchild = tmp->lchild;
    tmp->lchild = tr;

    //旋转完毕后,更新下高度值
    tr->height = 1 + std::max(GetHeight(tr->lchild), GetHeight(tr->rchild));
    tr->bf = CalCulateBF(tr);
    tmp->height = 1 + std::max(GetHeight(tmp->lchild), GetHeight(tmp->rchild));
    tmp->bf = CalCulateBF(tmp);

    tr = tmp;
}

/***
 * 左旋后右旋
 */
void LR_Rotate(AVLTree &tr)
{
    L_Rotate(tr->lchild);
    R_Rotate(tr);
}

/***
 * 右旋后左旋
 */
void RL_Rotate(AVLTree &tr)
{
    R_Rotate(tr->rchild);
    L_Rotate(tr);
}

/***
 * 往平衡二叉树插入节点
 */
bool InsertAVLTreeNode(AVLTree &tr, int x)
{
    if (!tr)
    {
        tr = (AVLNode *)malloc(sizeof(AVLNode));
        tr->rchild = tr->lchild = NULL;
        tr->data = x;
        tr->height = 1;
        tr->bf = 0;
    }
    else if (x < tr->data)
    {
        InsertAVLTreeNode(tr->lchild, x);
    }
    else if (x > tr->data)
    {
        InsertAVLTreeNode(tr->rchild, x);
    }
    else
    {
        return false; //树里有这个节点了,不插入
    }
    //每次插入完节点后,都自底向上地计算节点的高度和平衡因子。
    //可以知道,由于只有插入侧发生了变化,所以只需要依次去更新其路径上的节点的平衡因子和高度即可
    //正好,在递归计算的过程中,递归栈中也只会访问插入节点的祖先节点们
    tr->height = 1 + std::max(GetHeight(tr->lchild), GetHeight(tr->rchild)); //插入的节点高度自身加1,
    int balanceFactor = CalCulateBF(tr);
    tr->bf = balanceFactor;
    if (balanceFactor >= 2 && CalCulateBF(tr->lchild) >= 0)
    {
        std::cout << "右旋";
        R_Rotate(tr);
    }
    else if (balanceFactor >= 2 && CalCulateBF(tr->lchild) < 0)
    {
        std::cout << "LR_Rotate";
        LR_Rotate(tr);
    }
    else if (balanceFactor <= -2 && CalCulateBF(tr->rchild) <= 0)
    {
        std::cout << "左旋";
        L_Rotate(tr);
        //VisitLevelAVLTree(tr);
    }
    else if (balanceFactor <= -2 && CalCulateBF(tr->rchild) > 0)
    {
        std::cout << "RL_Rotate";
        RL_Rotate(tr);
    }
    else
    {
        std::cout << "不旋 ";
    }

    return true;
}

/***
 * 获取根节点tr的最小节点
 */
AVLNode *GetMinNode(AVLTree tr)
{
    if (tr->lchild == NULL)
        return tr;
    return GetMinNode(tr->lchild);
}

/**
 * 创建一颗二叉树
 */
AVLTree CreateBSTree()
{
    int input = 5;
    AVLTree tr = NULL;

    std::cout << "请输入树的节点值,输入0结束: ";
    std::cin >> input;
    while (input)
    {
        InsertAVLTreeNode(tr, input);
        std::cout << " "
                  << "目前层序遍历BST树序列:\n";
        VisitLevelAVLTree(tr);
        std::cout << std::endl;
        std::cout << "请输入树的节点值,输入0结束: ";
        std::cin >> input;
    }
    return tr;
}

/***
 * 获取某值所在的节点指针
 */
AVLNode *GetAVLNode(AVLTree tr, int x)
{
    if (tr)
    {
        if (x == tr->data)
        {
            return tr;
        }
        else if (x < tr->data)
        {
            return GetAVLNode(tr->lchild, x);
        }
        else if (x > tr->data)
        {
            return GetAVLNode(tr->rchild, x);
        }
    }
    return NULL;
}

/**
 * 从平衡树删除数字x的节点
 * 删除成功,返回节点,否则,返回NULL
 * 当我们删除一个节点的时候,待删除节点左右子树有一个为空,
 * 我们只需要将其不为空的子树的根节点提到待删除元素的位置即可。
 * 如果其左右子树都不为空,则将其右子树最小(或者左子树最大)的元素提到待删除节点处。
 */
AVLNode *RemoveAVLKey(AVLTree &tr, int x)
{
    if (!tr)
    {
        return NULL;
    }

    AVLNode *retNode;
    if (x < tr->data) //x小于根,向左找
    {
        tr->lchild = RemoveAVLKey(tr->lchild, x);
        retNode = tr;
    }
    else if (x > tr->data) //x大于根,向右找
    {
        tr->rchild = RemoveAVLKey(tr->rchild, x);
        retNode = tr;
    }
    else
    {
        if (!tr->lchild || !tr->rchild) //是叶子节点和单孩子,直接替换掉要删除的节点
        {
            std::cout << "删除叶子和单孩子节点 " << x << std::endl;
            AVLNode *tmp;
            if (!tr->lchild)
            { // 待删除节点左子树为空,直接将右孩子替代当前节点
                tmp = tr->rchild;
                tr->rchild = NULL;
                retNode = tmp;
            }
            else
            { // 待删除节点右子树为空,直接将左孩子替代当前节点
                tmp = tr->lchild;
                tr->lchild = NULL;
                retNode = tmp;
            }
        }
        else if (tr->lchild && tr->rchild)
        {
            //找到右子树最小的元素,替代待删除节点
            std::cout << "删除双孩子节点 " << x << std::endl;
            AVLNode *minNode = GetMinNode(tr->rchild); //找到一个后继节点
            if (minNode)
            {
                std::cout << "找到" << x << "后继节点:" << minNode->data << std::endl;
                minNode->rchild = RemoveAVLKey(tr->rchild, minNode->data); //删除tr的右子树里的minNode所在节点
                minNode->lchild = tr->lchild;
                //minNode->data = tr->data;

                tr->lchild = tr->rchild = NULL;
                retNode = minNode;
            }
        }
    }

    if (retNode == NULL)
        return NULL;

    retNode->height = 1 + std::max(GetHeight(retNode->lchild), GetHeight(retNode->rchild));
    int balanceFactor = CalCulateBF(retNode);
    tr->bf = balanceFactor;
    if (balanceFactor >= 2 && CalCulateBF(retNode->lchild) >= 0)
    {
        std::cout << "右旋";
        R_Rotate(retNode);
    }
    else if (balanceFactor >= 2 && CalCulateBF(retNode->lchild) < 0)
    {
        std::cout << "LR_Rotate";
        LR_Rotate(retNode);
    }
    else if (balanceFactor <= -2 && CalCulateBF(retNode->rchild) <= 0)
    {
        std::cout << "左旋";
        L_Rotate(retNode);
        //VisitLevelAVLTree(tr);
    }
    else if (balanceFactor <= -2 && CalCulateBF(retNode->rchild) > 0)
    {
        std::cout << "RL_Rotate";
        RL_Rotate(retNode);
    }
    else
    {
        std::cout << "不旋 ";
    }

    return retNode;
}

/**
 * 从平衡树删除数字x的节点
 * 删除成功,返回节点,否则,返回-1
 */
int Remove_Key(AVLTree &tr, int x)
{
    AVLNode *node = GetAVLNode(tr, x);
    if (node != NULL)
    {
        tr = RemoveAVLKey(tr, x);
        VisitLevelAVLTree(tr);
        return node->data;
    }
    else if (!node)
    {

        std::cout << "没有找到待删除节点 :" << x << std::endl;
        return -1;
    }
}

调用代码:

#include "AVLTree.h"
int main()
{
    AVLTree tr = CreateBSTree();
    VisitAVLTree(tr);
    Remove_Key(tr,99);
    Remove_Key(tr,15);
    Remove_Key(tr,77);
    Remove_Key(tr,43);
    Remove_Key(tr,87);
    Remove_Key(tr,5);
}

其他的一些相关的代码:

  1. 查找插入路径上,距离插入节点最近的平衡因子绝对值大于1的节点
  2. 二叉平衡树的高度和平衡因子计算
/*
 * 查找插入路径上,距离插入节点最近的平衡因子绝对值大于1的节点
 * 
 * 思路:利用简单的后序遍历的性质,后序遍历过程中,
 * 栈内会保存全部的根到目标节点的路径,
 * 返回 最先找到的祖先中的平衡因子为2的值
 */
AVLNode *FindNearBalValNode(AVLTree tr, int x)
{
    std::stack<AVLNode *> s;
    AVLNode *r = tr, *p = tr;
    while (p || !s.empty())
    {
        if (p)
        {
            s.push(p);
            p = p->lchild;
        }
        else
        {
            p = s.top();
            if (p->rchild && p->rchild != r)
            {
                //有右孩子,而且没有访问过右孩子
                p = p->rchild;
                s.push(p);
                p = p->lchild;
            }
            else
            {
                if (p->data == x) //找到了当前插入的数值,此时,stack栈里保存了根节点到p的全部路径节点
                {
                    std::cout << "root: ";
                    while (!s.empty())
                    {
                        p = s.top();
                        std::cout << p->data << " depth: " << p->height << " baVal:" << p->bf << " \n";
                        if (std::abs(p->bf) >= 2) //如果平衡因子大于或等于2,返回这个根节点
                            return p;
                        s.pop();
                    }
                    return NULL;
                }
                s.pop();
                r = p;
                p = NULL;
            }
        }
    }
    return NULL;
}


/**
 * 二叉平衡树的高度和平衡因子计算
 */
int CalcuBalanceValDepth(AVLTree tr)
{
    if (tr)
    {
        int hl = CalcuBalanceValDepth(tr->lchild) + 1;
        int hr = CalcuBalanceValDepth(tr->rchild) + 1;
        tr->height = std::max(hl, hr);
        tr->bf = hl - hr;
        return tr->height;
    }
    else
    {
        return 0;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值