AVL树性质和实现

AVL树

AVL是两名俄罗斯数学家的名字,以此纪念

与二叉搜索树的区别

AVL树在二叉搜索树的基础上增加了新的限制:需要时刻保证每个树中每个结点的左右子树高度之差的绝对值不超过1

因此,当向树中插入新结点后,即可降低树的高度,从而减少平均搜索长度

平衡因子

为了能方便处理平衡二叉搜索树的限制条件,通常会引入平衡因子的概念

某一节点的平衡因子=其右子树高度-其左子树高度

在AVL树中,并不是一定需要平衡因子的,有些代码的AVL树就没有平衡因子。

这里引入平衡因子只是更方便的去判断树是否平衡了

AVL树的效率推算

image-20231013200846180

我们知道树的增删查改的效率是与树的高度有关的

假如AVL树是满二叉树,此时:2h-1=N

假如AVL树不是满二叉树,设最底层的节点个数为X,此时:2h-X=N。X的范围为[1,最后一层结点树-1]

此时上述两种情况都可以得出树的高度的数量级为logN,因此增删查改的时间复杂度为O(logN)

AVL树的节点设计

首先,与二叉搜索树相比,每个节点多了一个平衡因子(balance factor)

其次,为了满足一定需求,还多了个_parent指针,指向节点的父亲

template<class K,class V>
struct AVLTreeNode
{pair<K, V> _kv;AVLTreeNode<K,V>* _left;AVLTreeNode<K,V>* _right;AVLTreeNode<K,V>* _parent;int _bf;//平衡因子AVLTreeNode(const pair<K,V>& kv):_kv(kv),_left(nullptr),_right(nullptr),_parent(nullptr),_bf(0){}
};

这是三叉链image-20231018232522035

AVL树的框架设计

template<class K,class V>
class AVLTree
{typedef AVLTreeNode<K, V> Node;
public://……
private:Node* _root=nullptr;
};

AVL树的插入

在面试时几乎不会让你手撕AVL树的插入。重要的是了解思想。

但让你手撕一个旋转是有可能的

插入的逻辑

AVL树是在二叉搜索树的基础上引入了平衡因子,因此AVL树的插入分为两个步骤:

  • 按照二叉搜索树的方式插入新节点(多了个链接_parent
  • 插入后,更新节点的平衡因子

首先注意:新插入的节点只会对其父亲和其祖先的平衡因子造成影响

插入节点的_parent的平衡因子是一定需要调整的;在插入之前其_parent的平衡因子有三个可能值1、-1、0,插入节点后:

  • 如果插入到_parent的左侧,则_parent的平衡因子–即可
  • 如果插入到_parent的右侧,则_parent的平衡因子++即可

此时:_parent的平衡因子可能有三大种情况:

  • 如果_parent的平衡因子为0,说明插入之前的平衡因子为正负1,插入后被调整成0,满足AVL树的性质,插入成功,插入结束
  • 如果_parent的平衡因子为±1,说明插入之前的平衡因子为0,此时树的高度增加,那么就需要继续向上更新祖先的平衡因子,直至某一祖先的平衡因子为0或者更新到根节点,才算插入成功,停止更新,插入结束。
  • 如果_parent的平衡因子为±2,此时违反了AVL树的性质,需要进行旋转处理。处理完成则算插入成功,插入结束
更新平衡因子

最坏的情况是一直更新到根,如下图:image-20231017154509542

因此在更新平衡因子时,我们的循环条件为:while(parent)

因为只有根节点的_parent为空。所以当更新完根节点的平衡因子后,循环结束

while (parent)
{if (cur == parent->_left)//节点插入在父亲左边{parent->_bf--;}else if (cur == parent->_right)//节点插入在父亲右边{parent->_bf++;}//进一步判断祖先节点的平衡因子if (parent->_bf == 0)//父亲的平衡因子为0,循环结束{break;}else if (parent->_bf == 1 || parent->_bf == -1)//父亲的平衡因子为1或-1,则需要继续向上调整{cur = parent;parent = cur->_parent;}else if (parent->_bf == 2 || parent->_bf == -2)//父亲的平衡因子为2或-2,则需要旋转,且选择完后树一定平衡,故结束循环{//左单旋if (parent->_bf == 2 && cur->_bf == 1){RotateL(parent);}//右单旋if (parent->_bf == -2 && cur->_bf == -1){RotateR(parent);}//右左双旋if (parent->_bf == 2 && cur->_bf == -1){RotateRL(parent);}//左右双旋if (parent->_bf == -2 &&	 cur->_bf == 1){RotateLR(parent);}break;}else//其他情况,此时说明在插入之前树就已经不是平衡树了{assert(false);}
}

最后一个else中的assert(false)看似是无用的,因为parent不可能是绝对值大于2的。但是代码都是人写的,不可排除一开始的树就是有问题的。因此这句代码很重要

AVL树的旋转

根据节点插入位置的不同,AVL树的旋转分为四种:

  • 新节点插入较高左子树的左侧–左左:右单旋
  • 新节点插入较高右子树的右侧–右右:左单旋
  • 新节点插入较高左子树的右侧—左右:先左单旋再右单旋(左右双旋)
  • 新节点插入较高右子树的左侧—右左:先右单旋再左单旋(右左双旋)

何时使用何种旋转:

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

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

    • 当pSubR的平衡因子为1时,执行左单旋

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

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

    • 当pSubL的平衡因子为-1是,执行右单旋

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

旋转完成后,原pParent为根的子树个高度降低,已经平衡,不需要再向上更新

右单旋

image-20231019154016105

上图在插入前,AVL树是平衡的。新节点插入到30的左子树(注意:此处不是左孩子)中,30左子树增加了一层,导致以60为根的二叉树不平衡,要让60平衡,只能将60左子树的高度减少一层,右子树增加一层,即将左子树往上提,这样60转下来,因为60比30大,只能将其放在30的右子树,而如果30有右子树,右子树根的值一定大于30,小于60,只能将其放在60的左子树,旋转完成后,更新节点的平衡因子即可

在旋转过程中,有以下几种情况需要考虑:

  1. cur节点的右孩子可能存在,也可能不存在

  2. parent可能是根节点,也可能是子树

    如果是根节点,旋转完成后,要更新根节点

    如果是子树,可能是某个节点的左子树,也可能是右子树

这两点是所有旋转情况都需要考虑的

右单旋的核心操作:把cur的右孩子给到parent的左,再把parent给到cur的右

代码:

void RotateR(Node* parent)//parent的平衡因子绝对值为2
{Node* cur = parent->_left;Node* curright = cur->_right;parent->_left = curright;//把cur的右孩子给到parent的左if (curright)//如果cur的右孩子存在,则更新其父亲为parent{curright->_parent = parent;}cur->_right = parent;//再把parent给到cur的右Node* ppnode = parent->_parent;//记录parent的原父亲节点,用于对cur的父亲进行更新parent->_parent = cur;//更新parent的父亲//对cur的父亲进行更新if (parent == _root)//parent即为根节点{_root = cur;cur->_parent = nullptr;//那么cur作为新的根,其父亲为空}else//parent是子树{if (ppnode->_left == parent){ppnode->_left = cur;}else{ppnode->_right = cur;}cur->_parent = ppnode;}cur->_bf = parent->_bf = 0;//更新完后,平衡因子一定为0
}
左单旋

image-20231019153929844

左单旋的核心操作与右单旋的核心操作正好是镜像的

左单旋的核心操作:把cur的左给parent的右,再把parent给到cur的左

代码:

void RotateL(Node* parent)
{Node* cur = parent->_right;Node* curleft = cur->_left;parent->_right = curleft;//把cur的左给parent的右if (curleft)//如果cur的左存在,则更新其父亲{curleft->_parent = parent;} cur->_left = parent;//再把parent给到cur的左Node* ppnode = parent->_parent;//记录parent的原父亲节点,用于对cur的父亲进行更新parent->_parent = cur;//对cur的父亲进行更新if (parent == _root){_root = cur;cur->_parent = nullptr;}else//说明左旋的部分只是某棵树的局部{if (ppnode->_left == parent){ppnode->_left = cur;}else{ppnode->_right = cur;}cur->_parent = ppnode;}cur->_bf = parent->_bf = 0;
}
右左双旋

image-20231019155022899

先对90进行右单旋,再对30进行左单旋

两次旋转。第一次旋转使其变成单纯的右边高,第二次旋转对应的左单旋

双旋代码难的不是旋转,而是平衡因子的更新

从图中可以看到:最终60成了根,60的左孩子给了parent的右边,60的右孩子给了cur的左边

因此平衡因子的更新分为两种情况:

  • h == 0:那么60则作为新插入的节点,此时60的bf == 0,那么parent和cur的bf也一定为0
  • h>=0:
    • 假如新结点插入在60的左边,即60的bf == -1。那么最终parent的bf == 0,cur的bf == 1
    • 假如新结点插入在60的右边,即60的bf == 1。那么最终parent的bf == -1,cur的bf == 0
    • 而60作为根节点最终bf一定为0

代码:

void RotateRL(Node* parent)
{Node* cur = parent->_right;Node* curleft = cur->_left;int bf = curleft->_bf;//先右旋再左旋RotateR(cur);RotateL(parent);//更新平衡因子if (bf == 0){parent->_bf = 0;cur->_bf = 0;curleft->_bf = 0;}else if (bf == 1){parent->_bf = -1;cur->_bf = 0;curleft->_bf = 0;}else if (bf == -1){parent->_bf = 0;cur->_bf = 1;curleft->_bf = 0;}else{assert(false);}
}
左右双旋

image-20231019160543333

与右左双旋是镜像的,不再赘述

代码:

void RotateLR(Node* parent)
{Node* cur = parent->_left;Node* curright = cur->_right;int bf = curright->_bf;//先左旋再右旋RotateL(cur);RotateR(parent);//更新平衡因子if (bf == 0){parent->_bf = 0;cur->_bf = 0;curright->_bf = 0;}else if (bf == 1){parent->_bf = 0;cur->_bf = -1;curright->_bf = 0;}else if (bf == -1){parent->_bf = 1;cur->_bf = 0;curright->_bf = 0;}else{assert(false);}
}
AVL树插入的完整代码
bool Insert(const pair<K, V>& kv)
{//先帮助插入节点找到正确位置if (_root == nullptr)//树为空{_root = new Node(kv);return true;}Node* cur = _root;Node* parent = nullptr;while (cur){//插入节点大于根节点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 (cur->_kv.first < parent->_kv.first){parent->_left = cur;}else{parent->_right = cur;}cur->_parent = parent;//控制平衡因子while (parent)//{if (cur == parent->_left)//节点插入在父亲左边{parent->_bf--;}else if (cur == parent->_right)//节点插入在父亲右边{parent->_bf++;}//进一步判断祖先节点的平衡因子if (parent->_bf == 0)//父亲的平衡因子为0,循环结束{break;}else if (parent->_bf == 1 || parent->_bf == -1)//父亲的平衡因子为1或-1,则需要继续向上调整{cur = parent;parent = cur->_parent;}else if (parent->_bf == 2 || parent->_bf == -2)//父亲的平衡因子为2或-2,则需要旋转,且选择完后树一定平衡,故结束循环{//左单旋if (parent->_bf == 2 && cur->_bf == 1){RotateL(parent);}//右单旋if (parent->_bf == -2 && cur->_bf == -1){RotateR(parent);}//右左双旋if (parent->_bf == 2 && cur->_bf == -1){RotateRL(parent);}//左右双旋if (parent->_bf == -2 &&	 cur->_bf == 1){RotateLR(parent);}break;}else//其他情况,此时说明在插入之前树就已经不是平衡树了{assert(false);}}}//左单旋
void RotateL(Node* parent)
{Node* cur = parent->_right;Node* curleft = cur->_left;parent->_right = curleft;if (curleft){curleft->_parent = parent;} cur->_left = parent;Node* ppnode = parent->_parent;//记录parent的原父亲节点parent->_parent = cur;//对cur的父亲进行更新if (parent == _root){_root = cur;cur->_parent = nullptr;}else//说明左旋的部分只是某棵树的局部{if (ppnode->_left == parent){ppnode->_left = cur;}else{ppnode->_right = cur;}cur->_parent = ppnode;}cur->_bf = parent->_bf = 0;
}//右单旋
void RotateR(Node* parent)
{Node* cur = parent->_left;Node* curright = cur->_right;//让右节点接到parent的左边,再将parent接到cur的右边parent->_left = curright;if (curright){curright->_parent = parent;}cur->_right = parent;Node* ppnode = parent->_parent;//记录parent的原父亲节点parent->_parent = cur;//对cur的父亲进行更新if (parent == _root){_root = cur;cur->_parent = nullptr;}else//说明右旋的部分只是某棵树的局部{if (ppnode->_left == parent){ppnode->_left = cur;}else{ppnode->_right = cur;}cur->_parent = ppnode;}cur->_bf = parent->_bf = 0;
}//右左双旋
void RotateRL(Node* parent)
{Node* cur = parent->_right;Node* curleft = cur->_left;int bf = curleft->_bf;//先右旋再左旋RotateR(cur);RotateL(parent);//更新平衡因子if (bf == 0){parent->_bf = 0;cur->_bf = 0;curleft->_bf = 0;}else if (bf == 1){parent->_bf = -1;cur->_bf = 0;curleft->_bf = 0;}else if (bf == -1){parent->_bf = 0;cur->_bf = 1;curleft->_bf = 0;}else{assert(false);}
}//左右双旋
void RotateLR(Node* parent)
{Node* cur = parent->_left;Node* curright = cur->_right;int bf = curright->_bf;//先左旋再右旋RotateL(cur);RotateR(parent);//更新平衡因子if (bf == 0){parent->_bf = 0;cur->_bf = 0;curright->_bf = 0;}else if (bf == 1){parent->_bf = 1;cur->_bf = -1;curright->_bf = 0;}else if (bf == -1){parent->_bf = 1;cur->_bf = 0;curright->_bf = 0;}else{assert(false);}
}

AVL树的验证

验证AVL树分为两步:

  • 验证其为二叉搜索树:如果中序遍历历可得到一个有序的序列,就说明为二叉搜索树(这里就不详细介绍了,详情可以看二叉搜索树那里)

  • 验证其为平衡树:

    • 每个节点子树高度差的绝对值不超过1(注意节点中如果没有平衡因子)
    • 节点的平衡因子是否计算正确
    int TreeHeight(Node* root)
    {if (root == nullptr)return 0;int leftHeight = TreeHeight(root->_left);int rightHeight = TreeHeight(root->_right);return rightHeight > leftHeight ? rightHeight + 1: leftHeight + 1;
    }bool IsBalance()//两个IsBalance构成重载
    {return IsBalance(_root);
    }bool IsBalance(Node* root)
    {if (root == nullptr)return true;int leftHeight = TreeHeight(root->_left);int rightHeight = TreeHeight(root->_right);if (rightHeight - leftHeight != root->_bf){cout << "平衡因子异常:" << root->_kv.first << "->" << root->_bf << endl;return false;}return abs(rightHeight - leftHeight) < 2&& IsBalance(root->_left)&& IsBalance(root->_right);
    }
    

    这里给出一个调试技巧:

    image-20231018182647226假如我运行出现了下面的情况

    image-20231018182657070

    我们现在知道在插入11的时候出了问题,那么就可以针对e=11时进行调试

    但如果现在有100个值,我在第99个值才出现问题,那是不是需要按F10按99次呢?

    两种方法,:

    • 一个是利用条件断点

      image-20231018183053645

    • 还一个是我们自己写代码让它停到想停的地方

      image-20231018183141199

      这里的int x=0;是随便写的,目的是能让断点在这里停下来。因为断点打在空行上是停不住的

      当然,我停在断点处不是为了调int x=0;,而是为了调下面

AVL树的删除

了解即可

image-20231019171629540

总结

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

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

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

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

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

相关文章

Leetcode2909. 元素和最小的山形三元组 II

Every day a Leetcode 题目来源&#xff1a;2909. 元素和最小的山形三元组 II 解法1&#xff1a;枚举 前后缀分解 定义 preMin[i] 为前缀最小值&#xff0c;初始化 preMin[0] nums[0]&#xff0c;递推公式&#xff1a;preMin[i] min(preMin[i - 1], nums[i])。 定义 suf…

InSAR 滤波算法

目录 1.InSAR 滤波原理 2.InSAR 滤波算法 2.1 均值滤波 2.2 Goldstein 滤波 2.3 改进的Goldstein 滤波 2.4 精致 Lee 滤波 2.5 小波滤波2.6 NL-InSAR 滤波 2.7 InSAR-BM3D 滤波 3.参考文献 本文由CSDN点云侠原创&#xff0c;爬虫网站请自重。 InSAR 滤波是InSAR 技术处理中的一…

rviz添加qt插件

一、增加rviz plugin插件 资料&#xff1a;http://admin.guyuehome.com/42336 https://blog.51cto.com/u_13625033/6126970 这部分代码只是将上面两个链接中的代码整合在了一起&#xff0c;整合在一起后可以更好的理解其中的关系 1、创建软件包 catkin_create_pkg rviz_tel…

【MySQL】查询语句

文章目录 选择语句 / 子句比较运算符AND&#xff0c;OR&#xff0c;NOT运算符IN运算符BETWEEN运算符LIKE运算符REGEXP运算符 选择语句 / 子句 USE&#xff1a;选择使用的databaseSELECT&#xff1a;选择查询的列FROM&#xff1a;选择查询的表WHERE&#xff1a;条件查询ORDER B…

IntelliJ IDEA 如何修改默认Maven仓库地址

在使用idea过程中&#xff0c;每次新建项目或者打开项目时&#xff0c;maven仓库地址都会变为默认地址。如何修改默认地址&#xff0c;让其保持不变&#xff0c;如下这种方式可以简单快捷的设置。 1.打开idea&#xff0c;取消项目自动加载 2.点击 Customize,然后再点击 All se…

DeepLearning - 余弦退火热重启学习率 CosineAnnealingWarmRestartsLR

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/134249925 CosineAnnealingWarmRestartsLR&#xff0c;即 余弦退火热重启学习率&#xff0c;周期性修改学习率的下降和上升&#xff0c;间隔幅度逐…

K7系列FPGA进行FLASH读写1——CCLK控制(STARTUPE2原语)

最近的工作涉及对 FPGA 进行远程更新&#xff0c;也就是通过远程通信接口将 .bin 文件送到 FPGA&#xff0c;然后写入 FLASH&#xff0c;这样当 FPGA 重新上电后就可以执行更新后的程序了。因此第一步工作就是进行 FLASH 的读写控制。 然而如果尝试配置 FLASH 管脚时&#xff0…

Android Datastore 动态创建与源码解析

涉及到的知识点 1、协程原理---->很好的博客介绍&#xff0c;一个小故事讲明白进程、线程、Kotlin 协程到底啥关系&#xff1f; 2、Channel知识点---->Android—kotlin-Channel超详细讲解 3、Coroutines : CompletableDeferred and structured concurrency 封装的DataS…

数学建模比赛中常用的建模提示词(数模prompt)

以下为数学建模比赛中常用的建模提示词&#xff0c;希望对你有所帮助&#xff01; 帮我总结一下数学建模有哪些预测类算法&#xff1f; 灰色预测模型级比检验是什么意思? 描述一下BP神经网络算法的建模步骤 对于分类变量与分类变量相关性分析用什么算法 前10年的数据分别是1&a…

javascript自定义事件的观察者模式写法和用法以及继承

<html><head><meta http-equiv"Context-Type:text/html;charsetutf-8"/><title>自定义事件之观察者模式</title><script type"text/javascript" src"common.js"></script></head><body>&…

avue中 curd的列表配置

说明&#xff1a; avue-crud组件中添加查询条件或者新增的时候&#xff0c;条件为下拉框且接口在curd组件中配置 1. html代码 <template><basic-container><avue-crud:data"dataList":option"option"search-change"searchChange&quo…

代码随想录 Day38 完全背包问题 LeetCode T70 爬楼梯 T322 零钱兑换 T279 完全平方数

前言 在今天的题目开始之前,让我们来回顾一下之前的知识,动规五部曲 1.确定dp数组含义 2.确定dp数组的递推公式 3.初始化dp数组 4.确定遍历顺序 5.打印dp数组来排错 tips: 1.当求取物品有限的时候用0-1背包,求取物品无限的时候用完全背包 结果是排列还是组合也有说法,当结果是组…

【PostgreSql基础语法 】1、增删改查、where、limit、like模糊查询

Shell命令框和Navicat联合使用 一、数据库层面&#xff08;shell命令行&#xff09;二、表格层面&#xff08;Navicat&#xff09;三、增删改查1. 增insert into2. 查询select3. UPDATE 改4. DELETE 删除 四、 关键字1. AND2.OR3. NOT NULL 和 NULL4. LIKE 模糊查询4.1 like查找…

linux boot阶段内存分配(x86)

x86中没有boot memory allocator&#xff0c;是用 memblock 来分配的。 memblock有memory 与reserved两种类型&#xff0c;它们的内存是静态内存&#xff0c;不需要用memblock本身去维护&#xff0c;它们被标记为__initdata_memblock&#xff0c;会在boot结束后&#xff08;fre…

渗透实战靶机2wp

0x00 简介 1、测试环境 目标IP&#xff1a;10.xxxx 测试IP&#xff1a;192.168.139.128 测试环境&#xff1a;win10、kali等 测试时间&#xff1a;2021.7.22-2021.7.22 测试人员&#xff1a;ruanruan 2、测试过程 本次实战主要通过对收集到的端口、目录等信息进行持续整…

润和软件HopeStage与奇安信网神终端安全管理系统、可信浏览器完成产品兼容性互认证

近日&#xff0c;江苏润和软件股份有限公司&#xff08;以下简称“润和软件”&#xff09;HopeStage 操作系统与奇安信网神信息技术&#xff08;北京&#xff09;股份有限公司&#xff08;以下简称“奇安信”&#xff09;终端安全管理系统、可信浏览器完成产品兼容性测试。 测试…

SQLSugar查询返回DataTable

SQLSugar是一个用于执行SQL查询的C#库&#xff0c;它提供了简单易用的API接口来执行SQL查询。要查询返回DataTable&#xff0c;可以使用SQLSugar的QueryHelper类。 以下是一个示例代码&#xff0c;展示了如何使用SQLSugar的QueryHelper类查询返回DataTable&#xff1a; 首先&…

SICP01(待续)

一、Lisp概览 语言&#xff1a;规则本身计算机科学的任务&#xff1a;形式化有关”怎么做“的指令性知识&#xff0c;并付诸实践问题产生&#xff1a;构建大型系统的时候难以管理解决方法&#xff1a;在大系统中控制复杂度的方法也是计算机所关注的注意&#xff1a;计算机中的…

阿里云二级域名绑定与宝塔Nginx反向代理配置

在阿里或者腾讯...各大域名商买好域名&#xff0c;备案解析好&#xff0c;目标URL&#xff0c;是真正的地址&#xff0c;比如一些端口&#xff0c;后者会自动填写。 注意ssl配置好&#xff0c;这里不要带反代端口

vue中异步更新$nextTick

1.需求 编辑标题, 编辑框自动聚焦 点击编辑&#xff0c;显示编辑框让编辑框&#xff0c;立刻获取焦点 2.代码实现 <template><div class"app"><div v-if"isShowEdit"><input type"text" v-model"editValue"…