平衡查找树(数据结构篇)

数据结构之平衡查找树

平衡查找树(AVL树)

概念

  • 为了防止因为插入删除而导致的树结构不平衡(通常我们删除节点总是对右子树的最小值节点替代操作,而不是交替的利用左子树的最大值节点替代,这就将导致左子树的平均深度大于右子树平均深度,直接导致整颗树平均深度的增加,导致对树节点的操作时间增长),导致对树的操作所用时间成倍数增长的问题,我们采用平衡二叉树—左右子树的平均深度差不多,达到一种平衡的操作,平衡二叉树希望对树的任意节点的操作时间为O(logN)
  • AVL(Adelson-Velskii和Landis)树是带有平衡条件的二叉查找树。这个平衡条件需要容易保持,而且需保证树的深度是O(logN)。
  • 一颗AVL树是其每个节点的左子树和右子树的高度最多差1的二叉查找树,空树的高度定义为-1
  • 在高度为h的AVL树中,最少节点数S(h)由S(h)=S(h-1)+S(h-2)+1算出。该h为该数最大高度。

旋转

  • 功能:对被破坏平衡的AVL树进行修正,使得重新平衡

  • 对于我们对树的插入操作破坏AVL树的平衡,我们则需要对树进行简单的修正来保持平衡,这个操作我们称其为旋转

  • 我们把必须重新平衡的节点(就是造成AVL树不平衡的插入点跟因此被打破平衡的另外一个节点(也就是插入点跟树中的一个节点高度差大于1)的共同根节点)叫做α,由于任意节点最多有两个儿子,因此高度不平衡时,α点的两颗子树的高度差2,这种不平衡的情形有四种:

    1.对α的左儿子的左子树进行一次插入

    2.对α的左儿子的右子树进行一次插入

    3.对α的右儿子的左子树进行一次插入

    4.对α的右儿子的右子树进行一次插入

  • 对于1和4的情形称为插入发生在"外边"的情况(即左-左的情况或右-右的情况),该情况需要通过对树的一次单旋转而完成调整

  • 对于2和3的情形称为插入发生在"内部"的情况(即左-右的情况或右-左的情况),这个情况需要通过对树的双旋转来处理

单旋转

  • 对于情形1(如图4-31),我们需要将造成不平衡的插入点上移一层,另外那个跟它高度差为2的节点下移一层(就是将α点跟其左节点进行单旋转(单旋转就是类似将左节点拎起来,其他的节点根据重量下坠,然后再根据二叉查找树的特性排序形成新的平衡二叉树)),并形成一个新的平衡二叉查找树,具体操作就如图4-32,插入6会破坏AVL树的特性,因为6是插入点,而8没有子节点(这个空的子节点其实就是被破坏平衡的高度差为2的节点),它们的共同根节点就为8,所以8为α点,我们需要将α点的左节点也就是7跟α点也就是8进行单旋转,然后形成新的平衡查找树

    具体操作为:

    1. 向α点的左儿子的左子树插入节点造成不平衡
    2. 将α点跟α点的左儿子做单旋转
    3. 原先α点的左儿子的左子树根节点成为现在α点(之前的α点的左儿子)的左儿子,原先的α点作为现在α点的右儿子,原先α点的左儿子的右子树根节点成为现在α点的右儿子的左子树根节点
    4. 重新形成一个新的AVL树

    image

    image

  • 对于情形4(如图4-33),其实情形4跟情形差不多的做法就是方向改变了,我们需要将造成不平衡的插入点上移一层,另外那个跟它高度差为2的节点下移一层(就是将α点跟其右节点进行单旋转(单旋转就是类似将右节点拎起来,其他的节点根据重量下坠,然后再根据二叉查找树的特性排序形成新的平衡二叉树)),并形成新的平衡二叉查找树

    具体操作为:

    1. 向α点的右儿子的右子树插入节点造成不平衡
    2. 对α点跟α点的右儿子进行单旋转
    3. 原先的α点成为现在的α点(之前的α点的右儿子)的左儿子,原先的α点的右儿子的右子树根节点成为现在的α点的右儿子,原先α点的右儿子的左子树根节点成为了现在α点的左儿子的右子树根节点
    4. 重新形成一颗新的AVL树

    image

双旋转

  • 对于情形2和3,单旋转无法修正被破坏的AVL树,对于图4-34子树Y太深,单旋转无法减低它的深度

    image

  • 对于情形2,例如图4-35(k1<k2<k3),我们需要用到双旋转(让α的左儿子的右子树根节点跟α的左儿子做一次单旋转,旋转完后,α的左儿子的右子树就是α的左儿子,原来α的左儿子变成了α的左儿子的右子树根节点,然后现在的α的左儿子(也就是原来的α的左儿子的右子树根节点)跟α点做一次单旋转),具体步骤就是

    1. 向α的左儿子的右子树插入节点
    2. 就双旋转后,让α的左儿子的右子树根节点(这里成为k2)旋转到α点(这里称为k3)的位置,α的左儿子(称为k1)成为k2的左儿子,k3成为k2的右儿子
    3. 如果插入的是左节点,就让它成为k1的右儿子,如果插入的是右节点就让它成为k3的左儿子。
    4. 然后就形成了新的AVL树

    image

  • 对于情形三(图4-36,k1<k2<k3),跟情形二做法差不多只是方向改变了,我们需要用到双旋转(让α的右儿子的左子树根节点跟α的右儿子做一次单旋转,旋转完后,α的右儿子的左子树就是α的右儿子,原来α的右儿子变成了α的右儿子的左子树根节点,然后现在的α的右儿子(也就是原来的α的右儿子的左子树根节点)跟α点做一次单旋转),具体步骤就是

    1. 向α的右儿子的左子树插入节点
    2. 就双旋转后,让α的右儿子的左子树根节点(这里成为k2)旋转到α点(这里称为k1)的位置,α的右儿子(称为k3)成为k2的右儿子,k1成为k2的左儿子
    3. 如果插入的是左节点,就让它成为k1的右儿子,如果插入的是右节点就让它成为k3的左儿子。
    4. 然后就形成了新的AVL树

    image

代码:

//平衡查找树是其每个节点的左子树和右子树的高度最多差1的二叉查找树
struct AVL{int data;AVL* left;AVL* right;int height;  //节点高度
};AVL* createNode(int data){auto p=new AVL;p->data=data;p->right=NULL;p->left=NULL;p->height=0;return p;
}//防止出现空的高度
int Height(AVL* root){if(root==NULL){return -1;}else{return root->height;}
}//左左情况单旋转函数
AVL* SingleRotatewithLeft(AVL* &k1){AVL* k2;k2=k1->left;k1->left=k2->right;k2->right=k1;k1->height= max(Height(k1->left), Height(k1->right))+1;k2->height= max(Height(k2->left), Height(k2->right))+1;return k2;
}//右右情况单旋转函数
AVL* SingleRotatewithRight(AVL* &k1){AVL* k2;k2=k1->right;k1->right=k2->left;k2->left=k1;k1->height= max(Height(k1->left), Height(k1->right))+1;k2->height= max(Height(k2->left), Height(k2->right))+1;return k2;
}//左右情况双旋转
AVL* DoubleRotatewithLeft(AVL* &k1){//非递归形式
//    AVL* k2;
//    AVL* k3;
//    k2=k1->left;
//    k3=k1->left->right;
//    k1->left=k3->right;
//    k2->right=k3->left;
//    k3->left=k2;
//    k3->right=k1;
//    k1->height=max(Height(k1->left), Height(k1->right))+1;
//    k2->height=max(Height(k2->left), Height(k2->right))+1;
//    k3->height=max(Height(k3->left), Height(k3->right))+1;
//    return k3;//递归形式,建议用递归,好记k1->left= SingleRotatewithRight(k1->left);   //k2跟k3单旋转return SingleRotatewithLeft(k1);       //k1跟现在的k2(原来的k3)单旋转
}//右左情况双旋转
AVL* DoubleRotatewithRight(AVL* &k1){//非递归形式
//    AVL* k2;
//    AVL* k3;
//    k2=k1->right;
//    k3=k1->right->left;
//    k1->right=k3->left;
//    k2->left=k3->right;
//    k3->left=k1;
//    k3->right=k2;
//    k1->height=max(Height(k1->left), Height(k1->right))+1;
//    k2->height=max(Height(k2->left), Height(k2->right))+1;
//    k3->height=max(Height(k3->left), Height(k3->right))+1;
//    return k3;//递归形式k1->right= SingleRotatewithLeft(k1->right);       //k2跟k3单旋转return SingleRotatewithRight(k1);           //k1跟现在的k2(原来的k3)单旋转
}//插入
AVL* insert(AVL* &root,int data){if(NULL==root){AVL* add= createNode(data);root=add;}else if(data<root->data){root->left= insert(root->left,data);//判断高度差if(2== Height(root->left)- Height(root->right)){//左左情况,情形1,单旋转if(data<root->left->data){root=SingleRotatewithLeft(root);}else{   //左右情况,情形2,双旋转root=DoubleRotatewithLeft(root);}}}else if(data>root->data){root->right= insert(root->right,data);if(2== Height(root->right)- Height(root->left)){//右右情况,情形4,单旋转if(data>root->right->data){root=SingleRotatewithRight(root);}else{    //右左情况,情形3,双旋转root=DoubleRotatewithRight(root);}}}//通过递归,从后序开始往上回,这样就能保证树的高度的正确性,这样子到了根节点就拿到了最大的高度root->height= max(Height(root->left), Height(root->right))+1;return root;
}//查找
AVL* Search(AVL* root,int data){if(NULL==root){return NULL;}if(data<root->data){root->left= Search(root->left,data);}else if(data>root->data){root->right= Search(root->right,data);}elsereturn root;
}//查找最小值
int findmin(AVL* root){//递归形式if(NULL==root){return root->data;}if(root->left!=NULL){return findmin(root->left);}else{return root->data;}//非递归形式
//    if(NULL!=root){
//        while (root->left!=NULL){
//            root=root->left;
//        }
//        return root->data
//    }
//    return NULL;
}//查找最大值
int findmax(AVL* root){//递归形式
//    if(NULL==root){
//        return NULL;
//    }
//    if(NULL!=root->right){
//        return findmax(root->right);
//    }else{
//        return root->data;
//    }//非递归形式if(NULL!=root){while (root->right!=NULL){root=root->right;}}return root->data;
}//删除节点,删除操作也会导致AVL树平衡破坏,所以删除操作也需要对AVL树做判断
AVL* del(AVL* &root,int data){if(NULL==root){return NULL;}else if(data<root->data){root->left= del(root->left,data);if(2== Height(root->left)- Height(root->right)){if(data<root->left->data){root= SingleRotatewithLeft(root);}else{root= DoubleRotatewithLeft(root);}}}else if(data>root->data){root->right=del(root->right,data);if(2== Height(root->right)- Height(root->left)){if(data>root->right->data){root= SingleRotatewithRight(root);}else{root= DoubleRotatewithRight(root);}}}else if(root->left&&root->right){int temp= findmin(root);root->data=temp;root->right=del(root->right,temp);}else{AVL* p=root;if(NULL==root->left){root=root->right;}else if(NULL==root->right){root=root->left;}delete p;}if(root!=NULL)root->height=max(Height(root->left), Height(root->right))+1;return root;
}//清空树
void destroy(AVL* &root){if(NULL==root){return;}destroy(root->left);destroy(root->right);delete root;
}//打印,中序遍历
void inorderprint(AVL* root){if(NULL==root){return;}inorderprint(root->left);cout<<root->data<<" ";inorderprint(root->right);
}

尾言

完整版笔记也就是数据结构与算法专栏完整版可到我的博客进行查看,或者在github库中自取(包含源代码)

  • 博客1: codebooks.xyz
  • 博客2:moonfordream.github.io
  • github项目地址:Data-Structure-and-Algorithms

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

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

相关文章

基于Java的高校校园点餐系统

开头语&#xff1a; 你好&#xff0c;我是计算机专业的学长&#xff0c;如果你对高校校园点餐系统感兴趣或有相关开发需求&#xff0c;欢迎联系我。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;JSP技术 工具&#xff1a;Eclipse、Tomcat 系统展示…

分布式用例执行

前言 这两天趁着有时间&#xff0c;我疯狂的码字了~~ 背景 我们公司是做人工智能平台的&#xff0c;什么是人工智能呢&#xff1f; 大数据 机器学习。大数据运行的基本就不快。机器学习算法运行起来也是慢的让人泪流满面。在我们的集群配置下&#xff0c;我使用一个 5M 的数…

基于IDEA的Maven(properties属性配置)

&#xff08;property &#xff1a;财产&#xff09;properties&#xff1a;它的复数。 同样也是基于上篇博客进行学习。&#xff08;具体的全部项目代码和结构可以去查看上篇...&#xff09; <properties><!--当前jdk版本 , 这一步可以完全省略--><maven.com…

2024青海三支一扶招1910人7月6日笔试

&#x1f4e2;2024年青海省三支一扶计划招募1910人公告已发布&#xff01; 小&#x1f004;️帮大家整理好了考试关键时间点&#xff1a; ★ 报名时间&#xff1a;6月20日至6月25日 ★ 报名网站&#xff1a;青海省人事考试信息网&#xff08;www.qhpta.com&#xff09; ★ 网上…

android studio构建项目报错Could not create an instance of type com.android.build.api.variant.impl.Applicat

Could not create an instance of type com.android.build.api.variant.impl.ApplicationVariantImpl 这个错误通常是由于Gradle插件版本不兼容导致的。你可能正在使用的Gradle插件版本与你的Android Studio版本不兼容。 要解决这个问题&#xff0c;你可以尝试以下解决方法&a…

音视频入门基础:H.264专题(3)——EBSP, RBSP和SODB

音视频入门基础&#xff1a;H.264专题系列文章&#xff1a; 音视频入门基础&#xff1a;H.264专题&#xff08;1&#xff09;——H.264官方文档下载 音视频入门基础&#xff1a;H.264专题&#xff08;2&#xff09;——使用FFmpeg命令生成H.264裸流文件 音视频入门基础&…

行为树行为树行为树

行为树由一个个节点组成 结构&#xff1a;树状结构运行流程&#xff1a;从根节点开始自顶向下往下遍历&#xff0c;每经过一个节点就执行节点对应的功能。 我们规定&#xff0c;每个节点都提供自己的excute函数&#xff0c;返还执行失败/成功结果。 然后根据不同节点的执行结…

国产数据库中读写分离实现机制

在数据库高可用架构下会存在1主多备的部署&#xff0c;备节点可以根据业务场景分发一部分流量以充分利用资源&#xff0c;并减轻主库的压力&#xff0c;因此在数据库的功能上需要读写分离来实现。 充分利用备节点的资源&#xff0c;提升业务的吞吐量&#xff1b;防止运维等非业…

《算法设计与分析》第五六章:回溯法与分支限界法

文章目录 回溯法分支限界法一、本章作业1.活动安排问题2.旅行商问题3.单源最短路径4.任务分配问题 二、算法积累1.回溯法求解01背包问题2.回溯法求解最大团问题3.回溯法求解n皇后问题4.回溯法求解地图着色5.回溯法求解哈密尔顿图6.回溯法求活动安排7.分支限界法求01背包问题8.分…

Flutter第十三弹 路由和导航

目标&#xff1a; 1.Flutter怎么创建路由&#xff1f; 2.怎么实现路由跳转&#xff1f;页面返回&#xff1f; 一、路由 1.1 什么是路由&#xff1f; 路由(Route)在移动开发中通常指页面&#xff08;Page&#xff09;&#xff0c;在Android中通常指一个Activity。所谓路由管…

小功率电机驱动方案中如何选择驱动IC

小功率电机驱动方案及驱动IC的选择 电机驱动作为工业4.0中工厂自动化整个闭环中的执行器环节&#xff0c;其性能好坏直接影响到整个闭环的性能。因此&#xff0c;工业4.0对电机驱动提出了更高的性能和功能要求&#xff0c;例如更快的响应速度、更高的带宽、更高精度的位置和速…

前端框架中的路由(Routing)和前端导航(Front-End Navigation)

聚沙成塔每天进步一点点 本文回顾 ⭐ 专栏简介前端框架中的路由&#xff08;Routing&#xff09;和前端导航&#xff08;Front-End Navigation&#xff09;1. 路由&#xff08;Routing&#xff09;1.1 定义1.2 路由的核心概念1.2.1 路由表&#xff08;Route Table&#xff09;1…

多模态大模型:基础架构

大模型技术论文不断&#xff0c;每个月总会新增上千篇。本专栏精选论文重点解读&#xff0c;主题还是围绕着行业实践和工程量产。若在某个环节出现卡点&#xff0c;可以回到大模型必备腔调或者LLM背后的基础模型重新阅读。而最新科技&#xff08;Mamba,xLSTM,KAN&#xff09;则…

gitee添加别人的仓库后,在该仓库里添加文件夹/文件

一、在指定分支里添加文件夹&#xff08;如果库主没有创建分支&#xff0c;自己还要先创建分支&#xff09; eg:以在一个项目里添加视图文件为例&#xff0c;用Echarts分支在usr/views目录下添加Echarts文件夹&#xff0c;usr/views/Echarts目录下添加index.vue 1.切换为本地仓…

Linux系统安装Ruby语言

Ruby是一种面向对象的脚本语言&#xff0c;由日本的计算机科学家松本行弘设计并开发&#xff0c;Ruby的设计哲学强调程序员的幸福感&#xff0c;致力于简化编程的复杂性&#xff0c;并提供一种既强大又易于使用的工具。其语法简洁优雅&#xff0c;易于阅读和书写&#xff0c;使…

重学java 66.IO流 转换流

且敬我疯狂&#xff0c;生命中不败的篇章 —— 24.6.11 一、字符编码 计算机中储存的信息都是用二进制数表示的&#xff0c;而我们在屏幕上看到的数字、英文、标点符号、汉字等字符是二进制数转换之后的结果。[按照某种规则&#xff0c;将字符存储到计算机中&#xff0c;称为编…

打造一个属于你的桌面天气 超级有个性的天气桌面

打造一个属于你的桌面天气 超级有个性的天气桌面。大家好&#xff0c;今天我们带来一个非常有趣的桌面天气工具&#xff0c;喜欢桌面diy的你&#xff0c;快点用上它吧&#xff01; 桌面上的美化&#xff0c;是许多爱美用户的心血和热爱。每个地方的美化&#xff0c;都是自己亲…

机器学习课程复习——隐马尔可夫

不考计算题 Q:概率图有几种结构? 条件独立性的公式? 顺序结构发散结构汇总结构Q:隐马尔可夫模型理解? 概念 集合:状态集合、观测集合 序列:状态序列、观测序列

linux环境编程基础学习

Shell编程&#xff1a; 相对的chmod -x xx.sh可以移除权限 想获取变量的值要掏点dollar&#xff08;&#xff04;&#xff09; 多位的话要加个花括号 运算&#xff1a;expr 运算时左右两边必须要加空格 *号多个含义必须加转义符 双引号可以加反单&#xff0c;但是发过来就不行 …

【android】安卓入门学习

文档介绍&#xff1a;http://8.136.122.222/book/primary/kotlin/kotlin-intro.html 文档补充说明&#xff1a;https://blog.csdn.net/qq_42059717/category_12047508.html 一、搭建环境及工具安装 见文档 二、工具界面及项目文件介绍 ├── app //工程主模块名称 │ …