数据结构 —— AVL树

目录

1. AVL的概念

2.AVL树的结构

3.AVL树的插入

3.1 平衡因子更新

4. 旋转

4.1 旋转的原则

 4.2 右单旋

4.2.1 右单旋代码实现

4.3 左单旋

4.3.1 左单旋代码实现

4.4 左右双旋

4.4.1 左右双旋代码实现

4.5 右左双旋

​编辑 4.5.1 右左双旋代码实现

5. AVL树的判断

6. AVL树的查找 


1. AVL的概念

1. AVL树是最先发明的⾃平衡⼆叉查找树,AVL是⼀颗空树或者具备下列性质的⼆叉搜索树:它的左右⼦树都是AV树,且左右⼦树的⾼度差的绝对值不超过1

     

2. AVL树是⼀颗⾼度平衡搜索⼆叉树,通过控制⾼度差去控制平衡
     
3. AVL树实现这⾥我们引⼊⼀个平衡因⼦(balance factor)的概念:每个结点都有⼀个平衡因⼦,任何结点的平衡因⼦等于右⼦树的⾼度减去左⼦树的⾼度,也就是说任何结点的平衡因⼦等于0/1/-1

     
AVL树并不是必须要平衡因⼦,但是有了平衡因⼦可以更⽅便我们去进⾏观察和控制树是否平衡,就像⼀个⻛向标⼀样
       
4. AVL树整体结点数量和分布和完全⼆叉树类似,⾼度可以控制在 logN ,那么增删查改的效率也可以控制在 O(logN) ,相⽐⼆叉搜索树有了本质的提升

   

5. 为什么AVL树是⾼度平衡搜索⼆叉树,要求⾼度差不超过1,⽽不是⾼度差是0呢?因为有些情况是做不到⾼度差是0的

    

比如⼀棵树是2个结点,4个结点等情况下,⾼度差最好就是1,⽆法作为⾼度差是0

 


2.AVL树的结构

//节点
template<class K, class V>
struct AVLTreeNode
{//需要parent指针,后续更新平衡因?可以看到 pair<K, V> _kv;AVLTreeNode<K, V>* _left;AVLTreeNode<K, V>* _right;AVLTreeNode<K, V>* _parent;//加上一个_parent构成三叉列int _bf; // balance factor 平衡因子AVLTreeNode(const pair<K, V>& kv):_kv(kv), _left(nullptr), _right(nullptr), _parent(nullptr), _bf(0){}
};//整棵树
template<class K, class V>
class AVLTree
{typedef AVLTreeNode<K, V> Node;
public:
private:Node* _root = nullptr;
};


3.AVL树的插入

1. 插⼊⼀个值按⼆叉搜索树规则进⾏插⼊

    
2. 新增结点以后,只会影响祖先结点的⾼度,也就是可能会影响部分祖先结点的平衡因⼦,所以更新从新增结点->根结点路径上的平衡因⼦,实际中最坏情况下要更新到根,有些情况更新到中间就可以停⽌了 (更新平衡因子

   
3. 更新平衡因⼦过程中没有出现问题,则插⼊结束(所有的平衡因子绝对值都是小于2的,新增节点的平衡因子一定是0,因为新增的左右孩子都为空

   
4. 更新平衡因⼦过程中出现不平衡,对不平衡⼦树进行旋转,旋转后本质调平衡的同时,本质降低了⼦树的⾼度,不会再影响上⼀层,所以插⼊结束

//按二叉搜索树规则进行插入
bool Insert(const pair<K, V>& kv)
{if (_root == nullptr){_root = new Node(kv);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(kv);if (parent->_kv.first < kv.first){parent->_right = cur;}else{parent->_left = cur;}//链接父节点cur->_parent = parent;return true;
}


3.1 平衡因子更新

更新原则:

   
1. 平衡因⼦ = 右⼦树⾼度-左⼦树⾼度(左 - 右 也可以)

   
2. 只有⼦树⾼度变化才会影响当前结点平衡因⼦

   
3. 插⼊结点,会增加⾼度,所以新增结点在parent的右⼦树,parent的平衡因⼦++,新增结点在parent的左⼦树,parent平衡因⼦--

    
4. 是否会继续往上更新取决于parent所在⼦树的⾼度是否变化

更新停⽌条件:

    
1. 更新后parent的平衡因⼦等于0,更新中parent的平衡因⼦变化为-1->0 或者 1->0,说明更新前parent⼦树⼀边⾼⼀边低新增的结点插⼊在低的那边插⼊后parent所在的⼦树⾼度不变,不会影响parent的⽗亲结点的平衡因⼦更新结束

    
2.  更新后parent的平衡因⼦等于1 或 -1,更新前更新中parent的平衡因⼦变化为0->1 或者 0->-1,说明更新前parent⼦树两边⼀样⾼,新增的插⼊结点后,parent所在的⼦树⼀边⾼⼀边低parent所在的⼦树符合平衡要求,但是⾼度增加了1,会影响arent的⽗亲结点的平衡因⼦,所以要继续向上更新

   
3. 更新后parent的平衡因⼦等于2 或 -2,更新前更新中parent的平衡因⼦变化为1->2 或者 -1->-2,说明更新前parent⼦树⼀边⾼⼀边低新增的插⼊结点在⾼的那边,parent所在的⼦树⾼的那边更⾼了,破坏了平衡,parent所在的⼦树不符合平衡要求,需要旋转处理

    

旋转的⽬标有两个:

                                1、把parent⼦树旋转平衡

                                2、降低parent⼦树的⾼度,恢复到插⼊结点以前的⾼度。所以旋转后也不需要继续往上更新,插⼊结束

//如果更新后的parent平衡因子为0,那么就不更新
if (parent->_bf == 0)
{break;
}
else if (parent->_bf == 1 || parent->_bf == -1)
{//如果更新后的parent平衡因子为1/-1,那么就继续向上更新cur = parent;parent = parent->parent;
}
else if (parent->_bf == 2 || parent->_bf == -2)
{//如果更新后的parent平衡因子为2/-2,那么就不符合平衡要求,需要继续旋转// 旋转break;
}
else
{//如果更新之后的平衡因子不为上面三种情况中的其中一种assert(false);
}

1. 更新到10结点,平衡因⼦为2,10所在的⼦树已经不平衡,需要旋转处理

2. 更新到中间结点,3为根的⼦树⾼度不变,不会影响上⼀层,更新结束

3. 最坏更新到根停⽌ 


4. 旋转

4.1 旋转的原则

1. 保持搜索树的规则

   
2. 让旋转的树从不平衡变平衡

    

3. 降低旋转树的高度,也就是恢复成插入之前的高度

    
旋转总共分为四种,左单旋/右单旋/左右双旋/右左双旋


 4.2 右单旋

1. 本图1展⽰的是10为根的树,有a/b/c抽象为三棵⾼度为h的⼦树(h>=0),a/b/c均符合AVL树的要求。10可能是整棵树的根,也可能是⼀个整棵树中局部的⼦树的根。这⾥a/b/c是⾼度为h的⼦树,是⼀种概括抽象表⽰,他代表了所有右单旋的场景,实际右单旋形态有很多种,具体图2/图3/图4/图5进⾏了详细描述

     
2.  在a⼦树中插⼊⼀个新结点,导致a⼦树的⾼度从h变成h+1,不断向上更新平衡因⼦,导致10的平衡因⼦从-1变成-2,10为根的树左右⾼度差超过1,违反平衡规则。10为根的树左边太⾼了,需要往右边旋转,控制两棵树的平衡

    
3. 旋转核⼼步骤,因为5 < b⼦树的值 < 10,将b变成10的左⼦树,10变成5的右⼦树,5变成这棵树新的根,符合搜索树的规则,控制了平衡,同时这棵的⾼度恢复到了插⼊之前的h+2,符合旋转原则。如果插⼊之前10整棵树的⼀个局部⼦树,旋转后不会再影响上⼀层,插⼊结束了 

图1 

 图2

图3

图4

图5


4.2.1 右单旋代码实现

下图的根节点的平衡因子为-2,即左子树深度过深,需要将左子树旋转,我们将根节点命名为parent,根节点的左节点命名为subL,parent的左孩子的右孩子命名为subLR,我们先将subLR接入parent的左节点,然后判断subLR是否为空,不为空就把subLR的父节点的指针指向parent,然后将subL调整为调整子树的根节点,最后更新平衡因子

//右单旋
void RotateR(Node* parent)
{//起始位置//subL是parent的左节点Node* subL = parent->_left;Node* subLR = subL->_right;//开始旋转//parent的左节点是subLRparent->_left = subLR;//如果subLR不为空  对subLR和parent的父节点进行更新if (subLR)subLR->_parent = parent;//subLR的父节点为parent//如果传入的根节点是一个子树的根节点则需要将旋转后的根节点重新接入原来的节点Node* pParent = parent->_parent;//subL作为新的根节点subL->_right = parent;parent->_parent = subL;//parent的根节点为subL//旋转的树可能是一个整棵树的子树,还需要和上一层进行链接//两种情况:1.parent之前就是根if (parent == _root){_root = subL;//subL作为新的根subL->_parent = nullptr;//subL为根,指向的parent置为空}else//不是根,那么就一定有parent的parent(pparent){//判断之前parent是pparent的左还是右if (pParent->_left == parent){pParent->_left = subL;}else{pParent->_right = subL;}//链接完之后将subL的Parent指向pParentsubL->_parent = pParent;}//更新平衡因子subL->_bf = 0;parent->_bf = 0;}


4.3 左单旋


1. 本图6展⽰的是10为根的树,有a/b/c抽象为三棵⾼度为h的⼦树(h>=0),a/b/c均符合AVL树的要求。10可能是整棵树的根,也可能是⼀个整棵树中局部的⼦树的根。这⾥a/b/c是⾼度为h的⼦树,是⼀种概括抽象表⽰,他代表了所有右单旋的场景,实际右单旋形态有很多种,具体跟上⾯左旋类似

   
2. 在a⼦树中插⼊⼀个新结点,导致a⼦树的⾼度从h变成h+1,不断向上更新平衡因⼦,导致10的平衡因⼦从1变成2,10为根的树左右⾼度差超过1,违反平衡规则。10为根的树右边太⾼了,需要往左边旋转,控制两棵树的平衡

   
3. 旋转核⼼步骤,因为10 < b⼦树的值 < 15,将b变成10的右⼦树,10变成15的左⼦树,15变成这棵树新的根,符合搜索树的规则,控制了平衡,同时这棵的⾼度恢复到了插⼊之前的h+2,符合旋转原则。如果插⼊之前10整棵树的⼀个局部⼦树,旋转后不会再影响上⼀层,插⼊结束了

图6


4.3.1 左单旋代码实现

左单旋和右单旋相反

//左单旋
void RotateL(Node* parent)
{Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;Node* parentParent = parent->_parent;subR->_left = parent;parent->_parent = subR;if (parentParent == nullptr){_root = subR;subR->_parent = nullptr;}else{if (parent == parentParent->_left){parentParent->_left = subR;}else{parentParent->_right = subR;}subR->_parent = parentParent;}parent->_bf = subR->_bf = 0;
}


4.4 左右双旋

通过图7和图8可以看到,左边⾼时,如果插⼊位置不是在a⼦树,⽽是插⼊在b⼦树,b⼦树⾼度从h变成h+1,引发旋转,右单旋⽆法解决问题,右单旋后,我们的树依旧不平衡。右单旋解决的纯粹的左边⾼,但是插⼊在b⼦树中,10为跟的⼦树不再是单纯的左边⾼,对于10是左边⾼,但是对于5是右边⾼,需要⽤两次旋转才能解决,以5为旋转点进⾏⼀个左单旋,以10为旋转点进⾏⼀个右单旋,这棵树这棵树就平衡了

图7 

图8

图7和图8分别为左右双旋中h==0和h==1具体场景分析,下⾯我们将a/b/c⼦树抽象为⾼度h的AVL⼦树进⾏分析,另外我们需要把b⼦树的细节进⼀步展开为8和左⼦树⾼度为h-1的e和f⼦树,因为我们要对b的⽗亲5为旋转点进⾏左单旋,左单旋需要动b树中的左⼦树。b⼦树中新增结点的位置不同,平衡因⼦更新的细节也不同,通过观察8的平衡因⼦不同,这⾥我们要分三个场景讨论

场景1:h >= 1时,新增结点插⼊在e⼦树,e⼦树⾼度从h-1并为h并不断更新8->5->10平衡因⼦,引发旋转,其中8的平衡因⼦为-1,旋转后8和5平衡因⼦为0,10平衡因⼦为1

     
场景2:h >= 1时,新增结点插⼊在f⼦树,f⼦树⾼度从h-1变为h并不断更新8->5->10平衡因⼦,引发旋转,其中8的平衡因⼦为1,旋转后8和10平衡因⼦为0,5平衡因⼦为-1

     
场景3:h == 0时,a/b/c都是空树,b⾃⼰就是⼀个新增结点,不断更新5->10平衡因⼦,引发旋转,其中8的平衡因⼦为0,旋转后8和10和5平衡因⼦均为0

和下面的图9结合看 

图9 

4.4.1 左右双旋代码实现

如果想区分上面的三种情况,只需要关注更新之后8这个节点的平衡因子 ,平衡因子更新之后是-1就是在e插入,是1就是在f插入,是0节点8自己就是新增

//左右双旋  先进行左旋再进行右旋
void RotateLR(Node* parent)
{Node* subL = parent->_left;Node* subLR = subL->_right;//双旋需要记录subLR的平衡因子int bf = subLR->_bf;//调用左右单旋RotateL(parent->_left);RotateR(parent);//按照图上的三种情况讨论 if (bf == -1)//如果节点8的平衡因子为-1(结合图看){//旋转之后subLR->_bf = 0;subL->_bf = 0;parent->_bf = 1;}else if (bf == 1){subLR->_bf = 0;subL->_bf = -1;parent->_bf = 0;}else if (bf == 0){subLR->_bf = 0;subL->_bf = 0;parent->_bf = 0;}else//出现错误直接断言,避免调试{assert(false);}}


4.5 右左双旋

跟左右双旋类似,下⾯我们将a/b/c⼦树抽象为⾼度h的AVL⼦树进⾏分析,另外我们需要把b⼦树的细节进⼀步展开为12和左⼦树⾼度为h-1的e和f⼦树,因为我们要对b的⽗亲15为旋转点进⾏右单旋,右单旋需要动b树中的右⼦树。b⼦树中新增结点的位置不同,平衡因⼦更新的细节也不同,通过观察12的平衡因⼦不同,这⾥我们要分三个场景讨论

场景1:h >= 1时,新增结点插⼊在e⼦树,e⼦树⾼度从h-1变为h并不断更新12->15->10平衡因⼦,引发旋转,其中12的平衡因⼦为-1,旋转后10和12平衡因⼦为0,15平衡因⼦为1

     
场景2:h >= 1时,新增结点插⼊在f⼦树,f⼦树⾼度从h-1变为h并不断更新12->15->10平衡因⼦,引发旋转,其中12的平衡因⼦为1,旋转后15和12平衡因⼦为0,10平衡因⼦为-1

     
场景3:h == 0时,a/b/c都是空树,b⾃⼰就是⼀个新增结点,不断更新15->10平衡因⼦,引发旋转,其中12的平衡因⼦为0,旋转后10和12和15平衡因⼦均为0 

 4.5.1 右左双旋代码实现

//右左双旋
void RotateRL(Node* parent)
{Node* subR = parent->_right;Node* subRL = subR->_left;int bf = subRL->_bf;RotateR(parent->_right);RotateL(parent);if (bf == 0){subR->_bf = 0;subRL->_bf = 0;parent->_bf = 0;}else if (bf == 1){subR->_bf = 0;subRL->_bf = 0;parent->_bf = -1;}else if (bf == -1){subR->_bf = 1;subRL->_bf = 0;parent->_bf = 0;}else{assert(false);}
}


5. AVL树的判断

int _Height(Node* root)
{if (root == nullptr){return 0;}int leftheight = _Height(root->_left);int rightheight = _Height(root->_right);return leftheight > rightheight ? leftheight + 1 : rightheight + 1;
}bool _IsAVLTree(Node* root)
{if (root == nullptr){return true;}int leftheight = _Heighr(root->_left);int rightheight = _Height(root->_right);int diff = rightheight - leftheight;if (abs(diff) >= 2){cout << root->_kv.first << "高度差异常" << endl;return false;}if (diff != root->_bf){cout << root->_kv.first << "平衡因子异常" << endl;}return _IsAVLTree(root->_left) && _IsAVLTree(root->_right);
}


6. AVL树的查找 

Node* Find(const K& key)
{Node* cur = _root;while (cur){if (cur->_kv.first < key){cur = cur->_right;}else if (cur->_kv.first > key){cur = cur->_left;}else{return cur;}}return nullptr;
}


完结撒花~

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

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

相关文章

交换机如何实现2.5G网络传输速率和网络变压器有关吗

华强盛电子导读&#xff1a;I19926430038 交换机实现2.5G网络传输速率涉及多个因素&#xff0c;其中包括硬件设计、端口支持、传输介质以及网络协议等。网络变压器在其中扮演了一个重要的角色&#xff0c;但并不是唯一的因素。 1. **硬件设计**&#xff1a;交换机需要有支持2.…

「C/C++」C++设计模式 之 抽象工厂模式(Abstract Factory)

✨博客主页何曾参静谧的博客&#x1f4cc;文章专栏「C/C」C/C程序设计&#x1f4da;全部专栏「VS」Visual Studio「C/C」C/C程序设计「UG/NX」BlockUI集合「Win」Windows程序设计「DSA」数据结构与算法「UG/NX」NX二次开发「QT」QT5程序设计「File」数据文件格式「PK」Parasoli…

【pycharm jupyter】启动报错

报错信息 upyter server process exited with code 1 ServerApp] A _jupyter_server_extension_points function was not found in jupyter_lsp. Instead, a _jupyter_server_extension_paths function was found and will be used for now. This function name will be depre…

精密机械代加工服务,为你的企业加速发展!

在当今竞争激烈的工业领域&#xff0c;精密机械代加工服务正成为众多企业实现快速发展的关键助力。这种专业服务凭借其独特的优势&#xff0c;为企业解决了诸多生产难题&#xff0c;推动企业在市场中大步向前。 先进的技术与设备 精密机械代加工服务提供商通常配备了先进的加工…

Data+AI━━揭秘千亿参数背后的技术较量:全球巨头的大模型布局!

DataAI━━揭秘千亿参数背后的技术较量&#xff1a;全球巨头的大模型布局&#xff01; 前言大语言模型:AI技术创新的新高地智能技术创新的"深水区"智能革命的下一站 前言 站在2024年科技创新的潮头&#xff0c;一场由大语言模型引发的技术革命正在上演。从ChatGPT引爆…

jmeter压测工具环境搭建(Linux、Mac)

目录 java环境安装 1、anaconda安装java环境&#xff08;推荐&#xff09; 2、直接在本地环境安装java环境 yum方式安装jdk 二进制方式安装jdk jmeter环境安装 1、jmeter单机安装 启动jmeter 配置环境变量 jmeter配置中文 2、jmeter集群搭建 多台机器部署jmeter集群…

草料二维码:低成本高效率的访客管理解决方案

在当前的商业和政治环境中&#xff0c;企业和政府机构越来越重视安全保密措施&#xff0c;尤其是对外来人员的行踪记录和管理。访客管理已成为企业运营中不可或缺的一环&#xff0c;它不仅提升了安全性&#xff0c;还增强了效率和便捷性。然而&#xff0c;许多机构仍在使用传统…

AOSP刷机

手机电脑都换了 从新复习一下吧 刷机分为几类 线刷 介质&#xff1a;通过USB数据线连接手机与电脑。 方法&#xff1a;通常使用专门的刷机工具&#xff08;如ADB、Fastboot、Odin等&#xff09;将ROM文件直接刷入设备。 优点&#xff1a;一般可以进行更全面的刷机操作&#x…

unocss 添加支持使用本地 svg 预设图标,并支持更改大小

安装 pnpm install iconify/utils 在配置文件 unocss.config.ts&#xff1a; presets > presetIcons 选项中 通过 FileSystemIconLoader 加载本地图标&#xff0c;并指定目录。 import presetWeapp from unocss-preset-weapp import { extractorAttributify, transformer…

一篇文章理解CSS垂直布局方法

方法1&#xff1a;align-content: center 在 2024 年的 CSS 原生属性中允许使用 1 个 CSS 属性 align-content: center进行垂直居中。 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewpo…

基于STM32+华为云IOT设计的大棚育苗管理系统

文章目录 一、前言1.1 项目介绍【1】项目开发背景【2】设计实现的功能【3】项目硬件模块组成 1.2 设计思路1.3 系统功能总结1.4 开发工具的选择【1】设备端开发【2】上位机开发 1.5 模块的技术详情介绍【1】NBIOT-BC26模块【2】MQ135传感器【4】SHT30传感器【5】B1750传感器 二…

小家电常用防水触摸IC

小家电常用防水触摸IC 挑战100个ICMAN“芯”实验-小家电常用防水触摸IC 电容式触摸芯片通常用于实现触摸控制&#xff0c;可轻松解决家用电器常见的触摸感应不灵敏和有水误触发的问题&#xff0c;优化了用户的交互使用体验&#xff0c;从而有效实现控制面板触摸按键的触摸感应和…

C++和OpenGL实现3D游戏编程【连载17】——着色器进阶

欢迎来到zhooyu的专栏。 主页网址&#xff1a;【zhooyu】 专栏网址&#xff1a;【C和OpenGL实现3D游戏编程】 &#x1f31f;&#x1f31f;&#x1f31f;这里将通过一个OpenGL实现3D游戏编程实例教程&#xff0c;带大家深入学习OpenGL知识。知识无穷而人力有穷&#xff0c;希望…

2342423

c语言中的小小白-CSDN博客c语言中的小小白关注算法,c,c语言,贪心算法,链表,mysql,动态规划,后端,线性回归,数据结构,排序算法领域.https://blog.csdn.net/bhbcdxb123?spm1001.2014.3001.5343 给大家分享一句我很喜欢我话&#xff1a; 知不足而奋进&#xff0c;望远山而前行&am…

《Python游戏编程入门》注-第4章8

2.2 “黄色炸弹”的实现 “黄色炸弹”实际上是不断重复地从屏幕顶端落下的黄色圆圈。 2.2.1 设置炸弹位置及下落速度 从图1中可以看出,“黄色炸弹”落下的位置(水平方向)是随机的,图8所示代码用来设置炸弹位置及下落速度。 图8 设置炸弹位置及下落速度的代码 其中,bom…

如何打开/关闭 GitLab 的版本检查功能?

本文分享如何打开/关闭 GitLab 的版本检查功能。 极狐GitLab 是 GitLab 的中国发行版【https://dl.gitlab.cn/ncecn6kb】&#xff0c;中文版本对中国用户更友好&#xff0c;文章以私有化部署的极狐GitLab 实例来演示版本检查功能的开启和关闭。强烈不建议关闭该功能&#xff0…

Linux——Ubuntu环境C编程

配置vim编辑器 设置一个tab键为4个空格 打开/etc/vim/vimrc文件&#xff0c;此文件为只读&#xff0c;所以要用sudo访问boot权限&#xff1a; set ts4&#xff08;设置一个tab键为4空格&#xff09; set nu&#xff08;vim编辑器下显示行号&#xff09; gcc编译器 gcc命令…

java访问华为网管软件iMaster NCE的北向接口

最近做的一个项目&#xff0c;需要读取华为一个叫iMaster NCE的网管软件的北向接口。这个iMaster NCE&#xff08;以下简称NCE&#xff09;用于管理项目的整个网络&#xff0c;尤其是光网络。业主要求我们访问该软件提供的对外接口&#xff0c;读取一些网络信息&#xff0c;比如…

ABeam 德硕 | 共探AI时代人才新生态,ABeam旗下德硕管理咨询(上海)有限公司荣膺2024杰出雇主!

ABeam News 近日&#xff0c;由HRoot主办的“2024中国人力资本发展大会”在上海市徐汇西岸艺术中心A馆盛大开幕并成功举办。大会以“智能协同 人才与AI的共生”为主题&#xff0c;作为咨询公司的ABeam中国&#xff0c;以优秀的人才管理理念及成果&#xff0c;受邀参会并荣获20…

App Store 截图生成器:轻松制作专业级应用营销图片

在线使用 &#x1f449; 立即使用截图生成器 简介 App Store 截图生成器是一个专门为 iOS 开发者和营销人员设计的在线工具&#xff0c;可以快速生成符合 App Store 规范的应用预览图片。无论是 iPhone 还是 iPad 应用&#xff0c;都能轻松创建出精美的营销截图。 主要特点…