浅谈【数据结构】树与二叉树之平衡二叉树

目录

1、平衡二叉树

2、平衡操作


谢谢帅气美丽且优秀的你看完我的文章还要点赞、收藏加关注

没错,说的就是你,不用再怀疑!!!

希望我的文章内容能对你有帮助,一起努力吧!!!


1、平衡二叉树

平衡二叉树(Balance Binary Tree):又称AVL树

它或者是一棵空树,或者是具有一下性质:

它的左子树和右子树本身又是一棵平衡二叉树,且左子树和右子树的深度之差的绝对值不会超过1

若平衡二叉树的结点的平衡因子定义为:

  • 该结点的左子树深度减去右子树深度
  • 则平衡二叉树上所有的平衡因子取值范围:-1 0 1
  • 只要有一个结点的平衡因子的绝对值是大于1的,那么这颗树就不是平衡的。

平衡二叉树通过平衡的二叉树排序树来实现的

2、平衡操作

场景:左深左插 :由于在左子树的左边增加一个新结点,导致当前结点的平衡因子大于1了

  • 单向右旋平衡处理

场景:右深右插 :由于在右子树的右边增加一个新结点,导致当前结点的平衡因子大于1了

  • 单向左旋平衡处理

***示例代码***

cpp 复制代码
#include <iostream>
#include <queue>


#define MAXLENGTH(l,r) (l>r?l:r)

// 树结点类型
typedef struct treenode
{
    int value;
    int height;
    struct treenode *lchild;
    struct treenode *rchild;
}Tree;

int getHeight(Tree *tree)
{
    if(!tree)
        return 0;
    return tree->height;
}

int getBalance(Tree *lchild,Tree *rchild)
{
    return getHeight(lchild) - getHeight(rchild);
}

/*
    @brief 增加新的结点进入二叉树中(循环法)
    @param tree 需要增加结点的树的根结点指针
    @param data 需要增加进树的新结点数据
    @return 返回树的根结点
*/
Tree *addNewNodeLoop(Tree *tree,int data)
{
    // 如果树为空作为根结点插入
    if(tree == nullptr)
    {
        Tree *node_ptr = new Tree;
        node_ptr->value = data;
        node_ptr->lchild = nullptr;
        node_ptr->rchild = nullptr;
        return node_ptr;   
    }

    // 如果树不为空的时候,需要和根结点进行比较
    Tree *father = nullptr; // 父结点
    Tree *current_node = tree; // 当前结点

    while(current_node)
    {
        father = current_node; // 将当前结点作为下一个结点的父结点
        // 当前结点的数据大于data,需要往左查
        if(current_node->value > data)
            current_node = current_node->lchild; // 将当前结点的左子结点作为新的根结点来继续下次比较
        // 当前结点的数据小于data,需要往右查
        else if(current_node->value < data)
            current_node = current_node->rchild; // 将当前结点的右子结点作为新的根结点来继续下次比较
        else
            return tree; // 不能存在相等情况,所以直接返回
    }

    // 出来循环:找到了插入的位置
    current_node = new Tree;
    current_node->value = data;
    current_node->lchild = nullptr;
    current_node->rchild = nullptr;

    // 确定是左插还是右插
    if(father->value > data)
        // 左插
        father->lchild = current_node;
    else
        // 右插
        father->rchild = current_node;

    return tree;
}

/*
    @brief 单向右旋平衡处理:左深左插
    @param k2 需要进行单向右旋平衡处理的结点
    @return 平衡处理完成之后,返回新的结点指针
*/
Tree *SingleRotateWidthRight(Tree *k2)
{
    // 获取k2的左子结点
    Tree *k1 = k2->lchild; 

    // k2的lchild指向k1的rchild
    k2->lchild = k1->rchild;

    // k1的rchild指向k2
    k1->rchild = k2;

    // 更新高度
    k2->height = MAXLENGTH(getHeight(k2->lchild),getHeight(k2->rchild))+1;
    k1->height = MAXLENGTH(getHeight(k1->lchild),getHeight(k1->rchild))+1;

    // 返回k1
    return k1;
}

/*
    @brief 单向左旋平衡处理:右深右插
    @param k2 需要进行单向左旋平衡处理的结点
    @return 平衡处理完成之后,返回新的结点指针
*/
Tree *SingleRotateWidthLeft(Tree *k2)
{
    // 获取k2的右子结点
    Tree *k1 = k2->rchild; 

    // k2的rchild指向k1的lchild
    k2->rchild = k1->lchild;

    // k1的lchild指向k2
    k1->lchild = k2;

    // 更新高度
    k2->height = MAXLENGTH(getHeight(k2->lchild),getHeight(k2->rchild))+1;
    k1->height = MAXLENGTH(getHeight(k1->lchild),getHeight(k1->rchild))+1;

    // 返回k1
    return k1;
}

/*
    @brief 双向旋转 - 先左后右
    @param k3 需要进行双向旋转平衡处理的结点
    @return 平衡处理完成之后,返回新的结点指针
*/
Tree *DoubleRotateLeftRight(Tree *k3)
{
    // 将k2进行左旋处理
    k3->lchild = SingleRotateWidthLeft(k3->lchild);

    // 上述操作:目的为了把k3从左深右插变成左深左插

    // 将k3进行右旋处理
    k3 = SingleRotateWidthRight(k3);

    return k3;
}


/*
    @brief 双向旋转 - 先右后左
    @param k3 需要进行双向旋转平衡处理的结点
    @return 平衡处理完成之后,返回新的结点指针
*/
Tree *DoubleRotateRightLeft(Tree *k3)
{
    // 将k2进行左旋处理
    k3->rchild = SingleRotateWidthRight(k3->rchild);

    // 上述操作:目的为了把k3从左深右插变成左深左插

    // 将k3进行右旋处理
    k3 = SingleRotateWidthLeft(k3);

    return k3;
}


/*
    @brief 增加新的结点进入二叉树中(递归法)
    @param tree 需要增加结点的树的根结点指针
    @param data 需要增加进树的新结点数据
    @return 返回树的根结点
*/
Tree *addNewNode(Tree *tree,int data)
{
    // 如果树为空作为根结点插入
    if(tree == nullptr)
    {
        Tree *node_ptr = new Tree;
        node_ptr->value = data;
        node_ptr->height = 1; // 它本身的高度
        node_ptr->lchild = nullptr;
        node_ptr->rchild = nullptr;
        return node_ptr;   
    }

    // 如果树不为空的时候,需要和根结点进行比较
    if(tree->value > data)
        // 左插
        tree->lchild = addNewNode(tree->lchild,data);
    else if(tree->value < data)
        // 右插
        tree->rchild = addNewNode(tree->rchild,data);

//------------------------------------------------------------------------------------------------
    // 更新高度:不管新结点有没有增加成功,都更新一下当前结点高度
    tree->height = MAXLENGTH(getHeight(tree->lchild),getHeight(tree->rchild)) + 1;
        
    // 计算平衡因子,来判断是否失衡
    // 先获取该节点的平衡因子
    int balance_dValue = getBalance(tree->lchild,tree->rchild);

    // 判断是否失衡
    if(abs(balance_dValue) > 1)
    {
        // 判断左右子树谁引起失衡
        if(balance_dValue > 0) // 左子树失衡:说明一个问题,左子树比右子树高:左深
        {
            // 判断是左插还是右插
            if(tree->lchild->value > data) // 左插
            {
                // 左深左插:单向右旋处理
                tree = SingleRotateWidthRight(tree); // tree的指向会发生改变
            }
            else // 右插
            {
                tree = DoubleRotateLeftRight(tree);
            }
        }
        else// 右子树失衡:说明一个问题,右子树比左子树高:右深
        {
            // 判断是左插还是右插
            if(tree->rchild->value < data) // 右插
            {
                std::cout << "右深插" << std::endl;
                // 右深右插:单向右旋处理
                tree = SingleRotateWidthLeft(tree); // tree的指向会发生改变
            }
            else // 左插
            {
               tree = DoubleRotateRightLeft(tree);
            }
        }
    }

    return tree;
}

/*
    @brief 创建一棵二叉排序树
    @return 成功返回创建好的树的首地址
*/
Tree *createNewTree()
{
    // 一棵空树
    Tree *tree = nullptr;

    while(1)
    {
        int data = -1;
        std::cin >> data;
        if(data == -1)
            break;

        // 插入到树中
        tree = addNewNode(tree,data);
    }

    // 返回创建好的树
    return  tree;
}

/*
    @brief 先序遍历二叉树的结点 根左右
    @param tree 需要先序遍历的二叉树根结点指针
*/
void frontPrintTree(Tree *tree)
{
    // 判断一下根结点是否为空
    if(tree == nullptr)
        return;
    
    // 把传入的结点直接作为根结点使用

    // 打印根结点
    std::cout << tree->value;

    // 打印左子树:这里的tree->lchild其实就是左子树的根
    frontPrintTree(tree->lchild);

    // 打印右子树:这里的tree->rchild其实就是右子树的根
    frontPrintTree(tree->rchild);
}

/*
    @brief 中序遍历二叉树的结点 左根右
    @param tree 需要先序遍历的二叉树根结点指针
*/
void middlePrintTree(Tree *tree)
{
    // 判断一下根结点是否为空
    if(tree == nullptr)
        return;
    
    // 把传入的结点直接作为根结点使用

    // 打印左子树:这里的tree->lchild其实就是左子树的根
    middlePrintTree(tree->lchild);

    // 打印根结点
    std::cout << tree->value;

    // 打印右子树:这里的tree->rchild其实就是右子树的根
    middlePrintTree(tree->rchild);
}

/*
    @brief 后序遍历二叉树的结点 左右根
    @param tree 需要先序遍历的二叉树根结点指针
*/
void backPrintTree(Tree *tree)
{
    // 判断一下根结点是否为空
    if(tree == nullptr)
        return;
    
    // 把传入的结点直接作为根结点使用

    // 打印左子树:这里的tree->lchild其实就是左子树的根
    backPrintTree(tree->lchild);

    // 打印右子树:这里的tree->rchild其实就是右子树的根
    backPrintTree(tree->rchild);

    // 打印根结点
    std::cout << tree->value;
}




int main()
{
    Tree *tree = createNewTree();
    
    frontPrintTree(tree);


    return 0;
}
相关推荐
xiaoshiguang33 小时前
LeetCode:222.完全二叉树节点的数量
算法·leetcode
爱吃西瓜的小菜鸡3 小时前
【C语言】判断回文
c语言·学习·算法
别NULL3 小时前
机试题——疯长的草
数据结构·c++·算法
TT哇3 小时前
*【每日一题 提高题】[蓝桥杯 2022 国 A] 选素数
java·算法·蓝桥杯
CYBEREXP20084 小时前
MacOS M3源代码编译Qt6.8.1
c++·qt·macos
ZSYP-S5 小时前
Day 15:Spring 框架基础
java·开发语言·数据结构·后端·spring
yuanbenshidiaos5 小时前
c++------------------函数
开发语言·c++
yuanbenshidiaos5 小时前
C++----------函数的调用机制
java·c++·算法
唐叔在学习5 小时前
【唐叔学算法】第21天:超越比较-计数排序、桶排序与基数排序的Java实践及性能剖析
数据结构·算法·排序算法
ALISHENGYA5 小时前
全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之分支结构(switch语句)
数据结构·算法