当前位置: 首页 > news >正文

【高阶数据结构】AVL树 {概念及实现;节点的定义;插入并调整平衡因子;旋转操作:左单旋,右单旋,左右双旋,右左双旋;AVL树的验证及性能分析}

AVL树

一、AVL树的概念

  • 二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种解决上述问题的方法:

  • AVL树:又被称为高度平衡搜索二叉树,当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。

一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:

  1. 左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)
  2. 它的左右子树都是AVL树

在这里插入图片描述

如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在log_2 n,搜索时间复杂度O(log_2 n)


二、AVL树节点的定义

template <class K, class V>
struct AVLTreeNode{    AVLTreeNode<K,V> *_left; //指向左节点的指针       AVLTreeNode<K,V> *_right; //指向右节点的指针                    AVLTreeNode<K,V> *_parent; //指向父节点的指针                       pair<K,V> _kv; //存储元素键值对                   int _bf; //平衡因子balance factor            AVLTreeNode(const pair<K,V> &kv)    :_left(nullptr),    _right(nullptr),                                                                         _parent(nullptr),    _kv(kv),    _bf(0)                                                                        {}    
};                                                                                

三、AVL树的插入

AVL树就是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看成是二叉搜索树。那么AVL树的插入过程可以分为三步:

  1. 按照二叉搜索树的方式插入新节点
  2. 调整节点的平衡因子
  3. 如果节点所在的二叉树不再平衡,通过旋转恢复平衡。

在这里插入图片描述

template <class K, class V>
bool AVLTree<K,V>::Insert(const pair<K,V> &kv)
{//1.按照二叉搜索树的方式插入新节点if(_root == nullptr){_root = new Node(kv);return true;}Node *cur = _root;Node *parent = nullptr; //cur要向下一直遍历到null,所以要记录父节点的指针while(cur != nullptr){if(kv.first > cur->_kv.first)                    {parent = cur;cur = cur->_right;}else if(kv.first < cur->_kv.first){parent  = cur;cur = cur->_left;}else{return false;}}cur = new Node(kv);if(kv.first > parent->_kv.first){                          parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent; //不要忘了修改父节点指针//2.调整节点的平衡因子while(parent!=nullptr) //只影响插入节点的所有祖先节点的平衡因子,parent不断向上一直遍历到根节点{//更新父节点的平衡因子//平衡因子=右树的高度-左树的高度if(cur == parent->_right){++parent->_bf; //插入右节点,bf++;      }else{--parent->_bf; //插入左节点,bf--;}//更新后检测双亲的平衡因子if(parent->_bf == 0){//由1/-1更新为0,说明以父节点为根的二叉树高度不变,无需继续向上调整。 break; }else if(abs(parent->_bf) == 1){//由0更新为1/-1,说明以父节点为根的二叉树高度增加了一层,需要继续向上调整。 parent = parent->_parent;cur = cur->_parent;}else if(abs(parent->_bf) == 2){//3.更新后为2/-2,说明parent所在的子树已经不平衡了,需要通过旋转恢复平衡。//......//下面的内容会有讲解↓↓↓}else{//除非代码有错,否则不可能有其他情况。assert(false);}}return true;
}

四、AVL树的旋转

如果在一棵原本是平衡的AVL树中插入一个新节点,可能造成不平衡,此时必须调整树的结构,使之平衡化。根据节点插入位置的不同,AVL树的旋转分为四种:

4.1 左单旋

新节点插入较高右子树的右侧—右右:左单旋

在这里插入图片描述

解释:

  1. 上图在插入前,AVL树是平衡的。a,b,c是高度为h的AVL子树(h>=0)。新节点插入到60的右子树c使c树增加了一层,最终导致以30为根的二叉树不平衡。
  2. 要让30平衡,就需要将30向左旋转,将60提上去。让30的左子树增加一层,右子树减少一层。也就是让30做60的左子树。
  3. 如果60有左子树b,b树的根一定大于30小于60,刚好做30的右子树。旋转完成后,更新节点的平衡因子即可。
  4. 在旋转过程中,有以下几种情况需要考虑:
    1. 60节点的左子树b可能存在,也可能为空。
    2. 30可能是根节点,也可能是子树
    • 如果是根节点,旋转完成后,要更新根节点指针_root。
    • 如果是子树,可能是某个节点的左子树,也可能是右子树。要更新父节点的指针。
template <class K, class V>
void AVLTree<K,V>::RotateL(Node *parent){ //parent对应30Node *subR = parent->_right; //subR对应60Node *subRL = subR->_left; //subRL对应b树的根Node *ppNode = parent->_parent; //记录30的父节点,便于旋转后进行连接。//30和b树进行连接parent->_right = subRL;         if(subRL != nullptr) //b树可能为空{subRL->_parent = parent;}//30和60重新连接subR->_left = parent;parent->_parent = subR;//60和30的父节点进行连接//如果30是根节点,更新根节点指针_root指向60//if(_root == parent)if(ppNode == nullptr){_root = subR;}else{//60和30的父节点进行连接,先要确定30是父节点的左子树还是右子树if(ppNode->_left == parent){ppNode->_left = subR;}else{                  ppNode->_right = subR;}}subR->_parent = ppNode;//更新平衡因子,进过旋转60和30的平衡因子变为0subR->_bf = parent->_bf = 0;
}

4.2 右单旋

新节点插入较高左子树的左侧—左左:右单旋

在这里插入图片描述

详细解释参考左单旋。

template <class K, class V>
void AVLTree<K,V>::RotateR(Node *parent){Node *subL = parent->_left;Node *subLR = subL->_right;Node *ppNode = parent->_parent;parent->_parent = subL;subL->_right = parent;                                                                                                         parent->_left = subLR;if(subLR != nullptr)subLR->_parent = parent;//if(_root == parent)if(ppNode == nullptr){_root = subL;}else{if(ppNode->_left == parent){ppNode->_left = subL;}else{ppNode->_right = subL;}}subL->_parent = ppNode;subL->_bf = parent->_bf = 0;
}

旋转的作用:1. 平衡二叉树 2. 降低二叉树高度(恢复到插入之前的高度h+2)


4.3 左右双旋

新节点插入较高左子树的右侧—左右:先左单旋再右单旋

在这里插入图片描述

将双旋变成单旋后再旋转,即:先对30进行左单旋,然后再对90进行右单旋,旋转完成后再考虑平衡因子的更新。

左右双旋又能细分为3种情况:

  1. a,b,c,d是空树60是新增,引发双旋。
  2. 在b树插入新增,引发双旋。
  3. 在c树插入新增,引发双旋。

三种情况的双旋过程不变,只是平衡因子的更新需要分别处理:

在这里插入图片描述

双旋的关键在于更新平衡因子,30,60,90三个节点的平衡因子都在两次单旋过程中被错误的置为0(因为并没要满足单旋的条件)。要根据以上三种不同的情况重新调整三个节点的平衡因子。如何区分三种不同的情况?根据旋转之前60的平衡因子确认。

template <class K, class V>
void AVLTree<K,V>::RotateLR(Node *parent){ //parent对应90Node *subL = parent->_left; //subL对应30Node *subLR = subL->_right; //subLR对应60int bf = subLR->_bf; //记录旋转之前60的平衡因子RotateL(subL); //30左单旋RotateR(parent); //90右单旋//更新平衡因子subLR->_bf = 0; //60的平衡因子一定为0     switch(bf) //根据旋转之前60的平衡因子确认属于那种情况{case 1:subL->_bf = -1;parent->_bf = 0;break;case -1:subL->_bf = 0;parent->_bf = 1;break;case 0:subL->_bf = 0;parent->_bf = 0;break;default://除非代码有错,否则不可能有其他情况。assert(false);break;}
}

双旋最终的结果是将60作为二叉树的根,60的左右子树分别作了30和90的右左子树。30和90作了60的左右子树。


4.4 右左双旋

新节点插入较高右子树的左侧—右左:先右单旋再左单旋

在这里插入图片描述

详细解释参考左右双旋。

template <class K, class V>
void AVLTree<K,V>::RotateRL(Node *parent){Node *subR = parent->_right;Node *subRL = subR->_left;int bf = subRL->_bf;RotateR(subR);RotateL(parent);  //更新平衡因子                  subRL->_bf = 0;switch(bf){case 1:subR->_bf = 0;parent->_bf = -1;break;case -1:subR->_bf = 1;parent->_bf = 0;break;case 0:subR->_bf = 0;parent->_bf = 0;break;default://除非代码有错,否则不可能有其他情况。assert(false);break;}
}

双旋最终的结果是将60作为二叉树的根,60的左右子树分别作了30和90的右左子树。30和90作了60的左右子树。


4.5 分情况旋转

    else if(abs(parent->_bf) == 2){ //3.更新后为2/-2,说明parent所在的子树已经不平衡了,需要通过旋转恢复平衡。if(parent->_bf == 2 && cur->_bf == 1) //右右,左单旋{RotateL(parent);}else if(parent->_bf == 2 && cur->_bf == -1) //右左,右左双旋{RotateRL(parent);}else if(parent->_bf == -2 && cur->_bf == -1) //左左,右单旋{RotateR(parent);                              }else if(parent->_bf == -2 && cur->_bf == 1) //左右,左右双旋{RotateLR(parent);}else{//除非代码有错,否则不可能有其他情况。assert(false);}break; //注意:旋转完成后,原parent为根的子树个高度降低,已经平衡,不需要再向上更新。}

总结:
假如以parent为根的子树不平衡,即parent的平衡因子为2或者-2,分以下情况考虑:

  1. parent的平衡因子为2,说明parent的右子树高,设parent的右子树的根为subR

    • 当subR的平衡因子为1时(右右),执行左单旋

    • 当subR的平衡因子为-1时(右左),执行右左双旋

  2. parent的平衡因子为-2,说明parent的左子树高,设parent的左子树的根为subL

    • 当subL的平衡因子为-1是(左左),执行右单旋

    • 当subL的平衡因子为1时(左右),执行左右双旋

经过旋转后可以直接break;因为经过旋转,插入元素前后子树的高度未发生变化都是h+2,不需要再调整上层节点的平衡因子。一次插入最多一次旋转。

所以,AVL树插入元素的时间复杂度:找插入位置O(log_2N) + 更新平衡因子O(log_2N) + 旋转O(1) = O(log_2N)。


五、AVL树的验证

AVL树是在二叉搜索树的基础上加入了平衡性的限制,因此要验证AVL树,可以分两步:

  1. 验证其为二叉搜索树
    如果中序遍历可得到一个有序的序列,就说明为二叉搜索树

  2. 验证其为平衡树

  • 每个节点子树高度差的绝对值不超过1

  • 节点的平衡因子是否计算正确

    template <class K, class V>
    bool AVLTree<K,V>::_Isbalance(Node *root){if(root == nullptr) return true; //注意,空树也是AVL树int lh = _Height(root->_left); //_Height返回二叉树的高度int rh = _Height(root->_right);int diff = rh-lh; //计算得到平衡因子                                                                                                              if(diff != root->_bf){cout << "Key: " << root->_kv.first << "bf: " << root->_bf << " 平衡因子异常" << endl;return false;}return abs(diff) < 2 && _Isbalance(root->_left) && _Isbalance(root->_right);
    }
    
  1. 测试用例

    //插入一两组序列测试
    void Test1(){//int arr[] = {16, 3, 7, 11, 9, 26, 18, 14, 15};int arr[] = {1,2,3,4,5,6,7,8,9,10};AVLTree<int, int> avl;for(auto e : arr){avl.Insert(make_pair(e, e));          }avl.Inorder();cout << "Isbalance: " << avl.Isbalance() << endl;
    }//插入10000个随机值测试
    void Test2(){srand(time(NULL));AVLTree<int, int> avl;const int N = 10000;for(int i = 0; i<N; ++i){int x = rand();avl.Insert(make_pair(x, i));}cout << "Isbalance: " << avl.Isbalance() << endl;
    }
    

六、AVL树的删除(了解)

AVL树节点的删除步骤如下:

  1. 在AVL树中找到要删除的节点。
  2. 如果要删除的节点是叶子节点,直接删除即可。
  3. 如果要删除的节点只有一个子节点,先使前驱节点指向该节点的子节点,然后删除该节点。
  4. 如果要删除的节点有两个子节点,需要找到该节点的替换节点(即该节点右子树中最小的节点或左子树中最大的节点),然后交换与替换节点的值,最后删除替换节点。
  5. 在删除节点后,需要更新从该节点到根节点路径上所有节点的平衡因子,并进行平衡调整,使得整棵树重新满足AVL树的性质。

删除操作的平衡调整方法和AVL树的插入操作相似,但在实现时需要注意一些细节上的差异。需要注意的是,删除操作可能会导致多个节点的平衡因子发生变化,因此需要一直向上循环更新和平衡调整,直到根节点。具体实现大家可以参考《算法导论》或《数据结构-用面向对象方法与C++描述》殷人昆版。


七、AVL树的性能

  • AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这样可以保证查询时高效的时间复杂度,即log_2 (N)

  • 但是如果要对AVL树做一些结构修改的操作,性能非常低下,比如:插入时要维护其绝对平衡,旋转的次数比较多;更差的是在删除时,有可能一直要让旋转持续到根的位置。

  • 因此,如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树。但一个结构经常修改,就不太适合。

相关文章:

【高阶数据结构】AVL树 {概念及实现;节点的定义;插入并调整平衡因子;旋转操作:左单旋,右单旋,左右双旋,右左双旋;AVL树的验证及性能分析}

AVL树 一、AVL树的概念 二叉搜索树虽可以缩短查找的效率&#xff0c;但如果数据有序或接近有序二叉搜索树将退化为单支树&#xff0c;查找元素相当于在顺序表中搜索元素&#xff0c;效率低下。因此&#xff0c;两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明…...

Netty—FuturePromise

Netty—Future&Promise 一、JDK原生 Future二、Netty包下的 Future三、Promise1、使用Promise同步获取结果2、使用Promise异步获取结果.3、使用Promise同步获取异常 - sync & get4、使用Promise同步获取异常 - await5、使用Promise异步获取异常 在异步处理时&#xff0…...

固定资产卡片乱怎么管理

固定资产卡片是记录公司固定资产信息的重要工具&#xff0c;如果管理不善&#xff0c;容易造成卡片混乱、数据错误等问题。 为了避免这种情况的发生&#xff0c;可以采取以下措施&#xff1a;  建立完善的资产管理制度&#xff0c;明确固定资产的分类、标准和使用情况&#x…...

AutoHotkey(AHK)脚本,自动截图当前屏幕并发送给微信窗口

前提先安装好 AutoHotkey &#xff0c;本脚本依赖AutoHotkey 环境 首先 &#xff0c;设置微信的快捷键 执行代码如下&#xff1a; Loop {SendInput, {Alt down}s{Alt up}Sleep, 2000 ; 等待2秒; 双击鼠标左键Click, 2Sleep, 1000 ; 等待1秒SendInput, {Alt down}a{Alt up}Sl…...

Golang - go build打包文件

Go编译打包文件 1、简单打包 程序 main1.go&#xff1a; package mainimport "fmt"func main() {fmt.Println("Hello World!") } 打包&#xff1a; # 在linux服务上执行下面的3个命令 # linux平台,生成main1可执行程序 CGO_ENABLED0 GOOSlinux GOARCHam…...

Java的归并排序

不爱生姜不吃醋⭐️⭐️⭐️ 如果本文有什么错误的话欢迎在评论区中指正 与其明天开始&#xff0c;不如现在行动&#xff01; 文章目录 &#x1f334;前言&#x1f334;一.归并排序1.概念2.时间复杂度3.代码实现 &#x1f334;二、小和问题1.概念2.举例3.代码实现 &#x1f334…...

B. The Walkway Codeforces Round 893 (Div. 2)

Problem - B - Codeforces 题目大意&#xff1a;小明在数轴上要从1走到n&#xff0c;其中某些坐标上有一些饼干店&#xff0c;共m个&#xff0c;小明身上也有无限多的饼干&#xff0c;它首先一定会在1的位置吃一个饼干&#xff0c;在每个饼干店的位置会吃一个&#xff0c;在前…...

第四篇 DirectShow 采集调用结构关系

第一篇: DirectShow视频采集_会头痛的可达鸭的博客-CSDN博客 一、GraphBuilder 1、IFilterGraph2、IGraphBuilder、ICaptureGraphBuiler2 (1)、CLSID IFilterGraph CLSID_FilterGraphIFilterGraph2 CLSID_CaptureGraphBuilderIGraphBuilder CL…...

2605. 从两个数字数组里生成最小数字

文章目录 Tag题目来源题目解读解题思路方法一&#xff1a;枚举比较法方法二&#xff1a;集合的位运算表示法 写在最后 Tag 【贪心】【位运算】【数组】 题目来源 2605. 从两个数字数组里生成最小数字 题目解读 给定两个各自只包含数字 1 到 9 的两个数组&#xff0c;每个数组…...

服务器发送事件Server-sent events详解与示例

Server-sent events 服务端进行数据推送除了WebSocket之外&#xff0c;还可以使用Server-Send-Event方案。 与 WebSocket不同的是&#xff0c;服务器发送事件是单向的。数据消息只能从服务端到发送到客户端&#xff08;如用户的浏览器&#xff09;。这使其成为不需要从客户端…...

SOLIDWORKS 多实体的建模方式

SOLIDWORKS多实体是SOLIDWORKS中一个非常有用的功能。在SOLIDWORKS中&#xff0c;对于模型的设定通常被大家所熟知的有以下几种类型&#xff1a;零件、装配体以及工程图。 其实还有一种划分&#xff0c;就是多实体。严格意义上来说&#xff0c;多实体既不属于零件也不属于装配体…...

NSSCTF web 刷题记录1

文章目录 前言题目[GXYCTF 2019]禁止套娃方法一方法二 [NCTF 2019]Fake XML cookbook[NSSRound#7 Team]ec_RCE[NCTF 2018]Flask PLUS 前言 今天是2023.9.3&#xff0c;大二开学前的最后一天。老实说ctf的功力还是不太够做的题目太少&#xff0c;新学期新气象。不可急于求成&am…...

遥感指数数据库

目前遥感指数多种多样&#xff0c;那怎么针对不同的应用领域选择合适的植被指数&#xff1f;不同卫星又有哪些可以反演的指数&#xff1f; Henrich等人开发了Index Database(网址&#xff1a;https://www.indexdatabase.de/)&#xff0c;总结了目前主流的遥感指数&#xff0c;…...

如何让insert程序速度快,可以试试联合SQL(insert 和 select 一起使用)?

查询添加可选择SQL执行&#xff0c;速度远超程序执行 insert 和 select案例 insert into 表1(列1,列2,列3,...) select 列1,列2,列3,...from表2(GROUP BY 列)116511 条数据 耗时45秒&#xff0c; 如果是程序查询然后再insert&#xff0c;则需要30分钟左右&#xff01;&#x…...

IP地址、网关、网络/主机号、子网掩码关系

一、IP地址 IP地址组成 IP地址分为两个部分&#xff1a;网络号和主机号 &#xff08;1&#xff09;网络号:标识网段&#xff0c;保证相互连接的两个网段具有不同的标识。 &#xff08;2&#xff09;主机号:标识主机&#xff0c;同一网段内&#xff0c;主机之间具有相同的网…...

使用skvideo.io.vread读取avi视频,报错“No way to determine width or height from video...”

问题描述&#xff1a; 一开始安装sk-video&#xff0c;在使用skvideo.io.vread读取avi视频&#xff0c;报错“No way to determine width or height from video. Need -s in inputdict. Consult documentation on I/O.” 解决方案&#xff1a; 1. 卸载sk-video pip uninsta…...

Nomad 系列-安装

系列文章 Nomad 系列文章 Nomad 简介 开新坑&#xff01;近期算是把自己的家庭实验室环境初步搞好了&#xff0c;终于可以开始进入正题研究了。 首先开始的是 HashiCorp Nomad 系列&#xff0c;欢迎阅读。 关于 Nomad 的简介&#xff0c;之前在 大规模 IoT 边缘容器集群管…...

网络版五子棋C++实现

目录 1.项目介绍 2.开发环境 3.核心技术 4.环境搭建 5.WebSocketpp介绍 5.1WebSocketpp是什么 5.2为什么使用WebSocketpp 5.3原理解析&#xff1a; 5.4WebSocketpp主要特性 6.WebSocketpp使用 7.JsonCpp使用 8.MySQL API 9.项目模块设计以及流程图 10.封装日志宏…...

项目招标投标公众号小程序开源版开发

项目招标投标公众号小程序开源版开发 以下是一个招标投标公众号小程序的功能列表&#xff1a; 用户注册与登录&#xff1a;用户可以注册账号并登录公众号小程序。项目发布&#xff1a;用户可以发布招标项目的详细信息&#xff0c;包括项目名称、招标单位、项目描述、招标要求…...

华为OD机试-机器人走迷宫

题目描述 机器人走一个迷宫,给出迷宫的x和y(x*y的迷宫)并且迷宫中有障碍物,输入k表示障碍物有k个,并且会将障碍物的坐标挨个输入. 机器人从0,0的位置走到x,y的位置并且只能向x,y增加的方向走,不能回退. 如代码类注释展示的样子,#表示可以走的方格,0代表障碍,机器人从0,0的位置…...

Chapter03-Authentication vulnerabilities

文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...

Vue记事本应用实现教程

文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展&#xff1a;显示创建时间8. 功能扩展&#xff1a;记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来

一、破局&#xff1a;PCB行业的时代之问 在数字经济蓬勃发展的浪潮中&#xff0c;PCB&#xff08;印制电路板&#xff09;作为 “电子产品之母”&#xff0c;其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透&#xff0c;PCB行业面临着前所未有的挑战与机遇。产品迭代…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具

文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

Spring Boot面试题精选汇总

&#x1f91f;致敬读者 &#x1f7e9;感谢阅读&#x1f7e6;笑口常开&#x1f7ea;生日快乐⬛早点睡觉 &#x1f4d8;博主相关 &#x1f7e7;博主信息&#x1f7e8;博客首页&#x1f7eb;专栏推荐&#x1f7e5;活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...

html-<abbr> 缩写或首字母缩略词

定义与作用 <abbr> 标签用于表示缩写或首字母缩略词&#xff0c;它可以帮助用户更好地理解缩写的含义&#xff0c;尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时&#xff0c;会显示一个提示框。 示例&#x…...

Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?

在大数据处理领域&#xff0c;Hive 作为 Hadoop 生态中重要的数据仓库工具&#xff0c;其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式&#xff0c;很多开发者常常陷入选择困境。本文将从底…...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题

分区配置 (ptab.json) img 属性介绍&#xff1a; img 属性指定分区存放的 image 名称&#xff0c;指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件&#xff0c;则以 proj_name:binary_name 格式指定文件名&#xff0c; proj_name 为工程 名&…...

C++:多态机制详解

目录 一. 多态的概念 1.静态多态&#xff08;编译时多态&#xff09; 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1&#xff09;.协变 2&#xff09;.析构函数的重写 5.override 和 final关键字 1&#…...

MySQL JOIN 表过多的优化思路

当 MySQL 查询涉及大量表 JOIN 时&#xff0c;性能会显著下降。以下是优化思路和简易实现方法&#xff1a; 一、核心优化思路 减少 JOIN 数量 数据冗余&#xff1a;添加必要的冗余字段&#xff08;如订单表直接存储用户名&#xff09;合并表&#xff1a;将频繁关联的小表合并成…...