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

news2024/11/18 3:20:27

目录

1、平衡二叉树

2、平衡操作


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

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

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


1、平衡二叉树

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

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

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

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

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

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

2、平衡操作

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

  • 单向右旋平衡处理

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

  • 单向左旋平衡处理

***示例代码***

#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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2075798.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【与C++的邂逅】--- C/C++内存管理

Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏&#xff1a; 与C的邂逅 C中我们总是提到管理资源&#xff0c;资源可以从内存中申请&#xff0c;前提是我们得知道C对内存管理的布局&#xff0c;本节我们就来学习这块…

民宿管理平台系统

你好&#xff0c;我是计算机专业的毕业生&#xff0c;专注于民宿管理平台系统的研究与开发。如果你对本系统感兴趣或有任何疑问&#xff0c;欢迎随时联系我。 开发语言 Java 数据库 MySQL 技术 SpringBoot框架 工具 ECLIPSE开发环境、Tomcat服务器 系统展示 首页 用户…

联发科双频Wi-Fi 6芯片MT7976CN全景图

这周末,除非外面下钞票,否则谁也拦不住我玩《黑神话悟空》(附:两款可以玩转悟空的显卡推荐) 天玑助力联发科力压高通~探秘MTK 5G旗舰智能手机SoC芯片——MT6989(天玑9300) 联发科双频Wi-Fi 6芯片MT7976CN全景图 TPLink AX3000路由器 TPLin

论文解读Multi-Prompt Alignment for Multi-Source Unsupervised Domain Adaptation

Multi-Prompt Alignment for Multi-Source Unsupervised Domain Adaptation NeurlIPS 2023 摘要 大多数现有的无监督域适应( UDA )方法依赖于共享网络来提取领域不变特征。无论如何&#xff0c;当面对多个源域时&#xff0c;优化这样的网络涉及更新整个网络的参数&#xff0…

推动RISC-V CPU性能快速提升并向上打开更多的高价值市场

作者&#xff1a;Imagination Technologies 8月21-23日&#xff0c;2024年RISC-V中国峰会在杭州黄龙饭店举行。作为已推出多款Imagination Catapult系列RISC-V CPU半导体知识产权&#xff08;IP&#xff09;的提供商&#xff0c;以及全球领先的GPU和AI加速器IP厂商&#xff0c…

【经验分享】CANOPEN协议驱动移植(基于CANfestival源码架构)

【经验分享】CANOPEN协议驱动移植(基于CANfestival源码架构&#xff09; 前言一、CANOPEN整体实现原理二、CANOPEN驱动收发三、Timer定时器四、Object Dictionary对象字典五、CANOPEN应用层接口六、CANOPEN 驱动移植经验总结 前言 本次CANOPEN移植基于CANfestival开源代码&…

开关电源中“黑箱”的考虑

在初设计阶段&#xff0c;首先要考虑开关电源的一些主要参数&#xff0c;这有助于设计者确 定自己所选的拓扑是否正确&#xff0c;也便于提前预定实验板所需的元器件。同时可以知 道接下来的设计所需的一些非常重 要的参数。关于如何对“黑箱”进 行估计&#xff0c;设计者只要…

MIPI联盟D-PHYv1.2规范阅读笔记

本文阅读自eetop.cn_mipi_D-PHY_specification_v1-2.pdf MIPI简介 MIPI 联盟成立至今制定了多种用于不同终端设备的接口标准&#xff0c;其中包括 用于摄像头的 CSI-2。 用于显示器的 DSI。 用于射频的 DigRF 。 用于麦克风的SLIMbus等接口协议。 MIPI CSI-2 协议简介 两…

书生大模型实战营(第三期闯关大挑战)- 进阶岛 第五关 茴香豆:企业级知识库问答工具

茴香豆本地标准版搭建 茴香豆介绍 茴香豆 是由书生浦语团队开发的一款开源、专门针对国内企业级使用场景设计并优化的知识问答工具。在基础 RAG 课程中我们了解到&#xff0c;RAG 可以有效的帮助提高 LLM 知识检索的相关性、实时性&#xff0c;同时避免 LLM 训练带来的巨大成…

8周流水6700美元Dropshipping运营全流程曝光丨出海笔记

&#xff08;之前删掉补发系列&#xff09; 之前分享了一个案例《净赚4000多美元&#xff01;个人卖家Dropshipping卖30天太阳镜&#xff0c;究竟如何做到的》&#xff0c;不少小伙伴觉得实操性很强&#xff0c;纷纷反馈意犹未尽&#xff0c;所以船长继续去找之前的Dropshippin…

房产系统技术功能解析

房产系统的功能设计旨在提高房地产行业的运作效率、优化资源分配&#xff0c;并为用户提供更便捷高效的服务体验。以下是房产系统关键技术功能的详细解析&#xff1a; 一、房源管理 房源信息录入与编辑&#xff1a;支持全面的房源信息录入&#xff0c;包括房屋位置、面积、户型…

C++第四十一弹---C++11新特性深度解析:让你的代码更现代、更高效(上)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】 目录 1. C11简介 2. 统一的列表初始化 2.1 &#xff5b;&#xff5d;初始化 2.2 std::initializer_list 3. 声明 3.1 auto 3.2 decltype 3.3 nu…

arcgis依据字段分组

脚本代码&#xff1a; UniqueDict {} def isDuplicateIndex(inValue): UniqueDict.setdefault(inValue,0) UniqueDict[inValue] 1 return UniqueDict[inValue] 输出值 isDuplicateIndex( !地块编号! )

临床试验中缺失数据的问题讨论

一、数据缺失的原因&#xff1a; &#xff08;1&#xff09;AE或疗效退出&#xff1b; &#xff08;2&#xff09;结局变量不适用&#xff08;无法获得结局变量&#xff09;&#xff1b; &#xff08;3&#xff09;失访&#xff1b; &#xff08;4&#xff09;数据采集失误&am…

云安全已经很好,但如何让它更好呢

尽管云计算很安全&#xff0c;但并不能完全避免数据泄露。随着云计算逐渐成为IT的重要部分&#xff0c;现在企业必须更认真地考虑如何加强云服务提供商默认安全基础设施的安全性。 传统云服务提供商都在努力为其客户提供强大的安全措施&#xff0c;他们通常会提供服务器端加密…

佰朔资本:市场转机正在逐步孕育 关注银行、电力等板块

商场起色正在逐渐孕育。中报宣布期即将以前&#xff0c;商场将完结盈利预期下修&#xff0c;一同美联储9月降息信号激烈&#xff0c;若协作国内方针加码发力信号&#xff0c;商场有望翻开向上空间。短期除高胜率的稳定盈利资产和中报超预期且景气继续方向&#xff0c;亦可注重中…

ES(索引数据库)导入MySQL全量(批量导入)和增量数据Canal增量数据同步利器

索引库数据管理 秒杀商品数量庞大&#xff0c;我们要想实现快速检索&#xff0c;不建议直接使用关系型数据库查找。不建议使用Redis缓存所有数据&#xff0c;因为秒杀商品量大&#xff0c;会影响Redis的性能&#xff0c;并且Redis的条件检索能力偏弱。我们可以使用Elasticsear…

Linux:NAT等相关问题

目录 1&#xff1a;NAT背景 2&#xff1a;NAT IP转换过程 3&#xff1a;NATP 4&#xff1a;正向代理 5&#xff1a;反向代理 6&#xff1a;NAT和代理服务器 应用场景 实现方法 1&#xff1a;NAT背景 IPv4地址耗尽&#xff1a;随着互联网的迅速发展&#xff0c;连接到…

[Leetcode 105][Medium] 从前序与中序遍历序列构造二叉树-递归

目录 一、题目描述 二、整体思路 三、代码 一、题目描述 题目地址 二、整体思路 前序遍历得到的是[根结点|左子树|右子树]&#xff0c;中序遍历得到的是[左子树|根结点|右子树] 那么可以设立一个递归函数&#xff0c;作用是利用前序遍历的数组和中序遍历的数组构建一个节点…

汇川技术|Inoproshop软件菜单[在线、调试]

哈喽&#xff0c;你好啊&#xff0c;我是雷工&#xff01; 现如今学习资源是容易获取了&#xff0c;像我网盘里堆了7T的资料&#xff0c;有很多还没看过&#xff0c;总是见到了就收藏起来&#xff0c;但是真的看不过来啊。有时间和精力的小伙伴可以找自己感兴趣的看起来。 本…