平衡二叉树(AVL树)C++

目录

AVL树的概念

AVL树的节点结构

AVL树的插入

更新平衡节点

代码实现

AVL树的旋转

左单旋

右单旋

左右双旋

右左双旋

AVL树的删除

AVL树的查找

AVL树的高度

AVL树的判定

AVL树的遍历


AVL树的概念

二叉排序(搜索)树,虽然可以缩短查找的效率,但是在数据接近有序的时候,二叉排序树会退化成为单支树(斜树),相当于在顺序表中查找元素,效率低下。

因此,两位俄罗斯的数学家发明了一种解决上述问题的办法,向二叉搜索树插入新的节点后,通过调整保证左右子树的高度差绝对值不大于1,从而降低树的高度,减少平均搜索长度。

平衡二叉树(Balanced Binary Tree)是一种特殊的二叉排序树,又称AVL树。

AVL树的性质:

1.AVL树是二叉排序树

2.左右子树高度之差(即平衡因子)的绝对值不超过1,即平衡因子可能为-1,0,1,

AVL树的节点结构

用KV模型定义二叉树,把节点定义成三叉链结构,因为构造的新节点左右子树都为空树,所以平衡因子设为0

平衡因子:右子树的高度-左子树的高度

template<class K,class V>
struct AVLTreeNode 
{//存储的键值对pair<K, V> _kv;//平衡因子(balance factor)int _bf;//存储的三叉链AVLTreeNode<K, V>* _parent;//方便找到父节点AVLTreeNode<K, V>* _left;AVLTreeNode<K, V>* _right;//构造函数AVLTreeNode(const pair<K, V>& kv):_kv(kv), _parent(nullptr), _left(nullptr), _right(nullptr), _bf(0){}
};

AVL树的插入

更新平衡节点

AVL树插入结点时有以下三个步骤:

1.按照二叉搜索树的插入方法,找到待插入位置。
2.找到待插入位置后,将待插入结点插入到树中。
3.更新平衡因子,如果出现不平衡,则需要进行旋转。

与二叉搜索树相比,多了更新平衡因子这个步骤。因为插入节点后树的高度可能会变化

更新父节点的平衡因子(一定要更新)

1.左边插入:parent的平衡因子--

2.右边插入:parent的平衡因子++

根据父节点的平衡因子,判断是否要更新祖先节点的平衡因子

1.如果父节点bf==0,说明父节点原来一定是-1或+1,插入的节点弥补了原来的空缺,树的高度没有改变,不用向上更新

2.如果为+-1,说明平衡因子从0变成了-1,+1,高度改变,所以要向上更新

3.如果为+-2,说明该父节点就是最小不平衡子树的根节点,不用再向上查找

 

 

代码实现

class AVLTree
{typedef AVLTreeNode<K, V> Node;
private:Node* _root;public:AVLTree():_root(nullptr){}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 (cur->_kv.first < kv.first) {parent = cur;cur = cur->_left;}else if (cur->_kv.first > kv.first) {parent = cur;cur = cur->_right;}else {return false;}}cur = new Node(kv);//链接并更新if (cur->_kv.first < parent->_kv.first) {parent->_left = cur;cur->_parent = parent;parent->_bf--;}else{parent->_right = cur;cur->_parent = parent;parent->_bf++;}while (parent)//最远要更新到根节点{if (parent->_bf == 0) {break;}else if (abs(parent->_bf) == 1){cur = cur->_parent;parent = parent->_parent;//向上查找}else if(abs(parent->_bf)==2){//判断是哪一种旋转情况if (parent->_bf==2&&cur->bf==1){rotateL(parent);}else if (parent->_bf == -2 && cur->bf == -1){rotateR(parent);}else if (parent->_bf == -2 && cur->bf == 1){rotateLR(parent);}else if (parent->_bf == 2 && cur->bf == -1){rotateRL(parent);}break;}else{//如果程序走到这里,说明在此之前就有不平衡的子树assert(false);}}return true;}
};

AVL树的旋转

左单旋

什么时候需要用到左旋操作呢?

当 parent 的平衡因子为 2,cur 的平衡因子为 1 时,需要进行左单旋。

并且经过左单旋后,树的高度没有发生变化,所以左单旋后无需继续往上更新平衡因子。

请添加图片描述

在这里插入图片描述

 在这里插入图片描述

 步骤:

  • 先让 subR 的左子树(subRL)作为 parent 的右子树。
  • 然后让 parent 作为 subR 的左子树。
  • 接下来让 subR 作为整个子树的根。
  • 最后更新平衡因子。

 代码实现

void rotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;Node* pparent = parent->_parent;//1.建立subRL与parent之间的关系//左子树滑动parent->_right = subRL;if (subRL) {subRL->_parent = parent;}//2.建立subR和parent之间的关系//更新右子树的左子树subR->_left = parent;parent->_parent = subR;//3.建立pparent和subR之间的关系//与上一个节点建立连接if (parent == _root){_root = subR;subR->_parent == nullptr;}else{subR->_parent = pparent;if (parent = pparent->_left) {pparent->_left = subR;}else{pparent->_right = subR;}}//更新平衡因子parent->_bf = 0;subR->_bf = 0;}

右单旋

请添加图片描述

  • 先让 subL 的右子树(subLR)作为 parent 的左子树。
  • 然后让 parent 作为 subL 的右子树。
  • 接下来让 subL 作为整个子树的根。
  • 最后更新平衡因子。
void rotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;Node* pparent = parent->_parent;//滑动parent->_left = subLR;if (subLR) {subLR->_parent = parent;}//更新左子树的右子树subL->_right = parent;parent->_parent = subL;if (parent == _root){_root = subL;subL->_parent = nullptr;}else {subL->_parent == pparent;if (parent = pparent->_left) {pparent->_left = subL;}else{pparent->_right = subL;}}parent->_bf = 0;subL->_bf = 0;}

左右双旋

(1)以30为节点进行左单旋

在这里插入图片描述

 (2)以90为节点进行右单旋

 

左右双旋后,平衡因子的更新随着 subLR 原始平衡因子的不同分为以下三种情况:

(1)当 subLR 原始平衡因子是 -1 时,左右双旋后 parent、subL、subLR 的平衡因子分别更新为 1、0、0

在这里插入图片描述

(2)当 subLR 原始平衡因子是 1 时,左右双旋后 parent、subL、subLR 的平衡因子分别更新为 0、-1、0

(3)当 subLR 原始平衡因子是 0 时(说明 subLR 为新增结点),左右双旋后 parent、subL、subLR 的平衡因子分别更新为0、0、0

可以看到,经过左右双旋后,树的高度没有发生变化,所以左右双旋后无需继续往上更新平衡因子。

代码实现:

void rotateLR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;int bf = subLR->_bf;rotateL(subL);//parent并不为最小不平衡子树的根,所以要在旋转以后重新赋值rotateR(parent);//各点的bf值由调整前subLR的bf值决定if (_bf == 0) {parent->_bf = 0;subL->_bf = 0;subLR->_bf = 0;}else if (bf == 1){parent->_bf = 0;subL->_bf = -1;subLR->_bf = 0;}else if (bf == -1){parent->_bf = 1;subL->_bf = 0;subLR->_bf = 0;}else{assert(false);}}

右左双旋

(1)以 90 为旋转点进行右单旋

在这里插入图片描述

(2)以 30 为旋转点进行左单旋

 

右左双旋后,平衡因子的更新随着 subLR 原始平衡因子的不同分为以下三种情况:、

(1)当 subRL 原始平衡因子是 1 时,左右双旋后 parent、subR、subRL 的平衡因子分别更新为 -1、0、0

 

(2)当 subRL 原始平衡因子是 -1 时,左右双旋后 parent、subR、subRL 的平衡因子分别更新为 0、1、0

 

(3)当 subRL 原始平衡因子是 0 时(说明 subRL为新增结点),左右双旋后 parent、subR、subRL 的平衡因子分别更新为0、0、0

 

可以看到,经过右左双旋后,树的高度没有发生变化,所以右左双旋后无需继续往上更新平衡因子。
 

代码实现:

void rotateRL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;int bf = subRL->_bf;rotateR(subR);rotateL(parent);if (_bf == 0) {parent->_bf = 0;subR->_bf = 0;subRL->_bf = 0;}else if (bf == 1){parent->_bf = -1;subR->_bf = 0;subRL->_bf = 0;}else if (bf == -1){parent->_bf = 0;subR->_bf = 1;subRL->_bf = 0;}else{assert(false);}}

AVL树的删除

要进行结点的删除,首先需要在树中找到对应key值的结点,寻找待删除结点的方法和二叉搜索树相同

1.先找到待删除的结点。
2.若找到的待删除结点的左右子树均不为空,则需要使用替换法进行删除。

替换法删除指的是:让待删除结点左子树当中key值最大的结点,或待删除结点右子树当中值最小的结点代替待删除结点被删除(代码中以后者为例),然后再将待删除结点的key值以及value值都改为代替其被删除的结点的值即可。

也就是说,我们最终找到的实际被删除的结点的左右子树当中至少有一个为空树。

在找到实际需要被删除的结点后,我们先不进行实际的删除,而是先进行平衡因子的更新,不然后续更新平衡因子时特别麻烦(已经尝试过),而更新平衡因子时的规则与插入结点时的规则是相反的,更新规则如下:

1.删除的结点在parent的右边,parent的平衡因子− − 。
2.删除的结点在parent的左边,parent的平衡因子+ +。

并且每更新完一个结点的平衡因子后,都需要进行以下判断:

1.如果parent的平衡因子等于-1或者1,表明无需继续往上更新平衡因子了。
2.如果parent的平衡因子等于0,表明还需要继续往上更新平衡因子。
3.如果parent的平衡因子等于-2或者2,表明此时以parent结点为根结点的子树已经不平衡了,需要进行旋转处理。

在旋转处理时,分为以下6种情况

1.当parent的平衡因子为-2,parent的左孩子的平衡因子为-1时,进行右单旋。
2.当parent的平衡因子为-2,parent的左孩子的平衡因子为1时,进行左右双旋。
3.当parent的平衡因子为-2,parent的左孩子的平衡因子为0时,也进行右单旋,但此时平衡因子调整与之前有所不同,具体看代码。
4.当parent的平衡因子为2,parent的右孩子的平衡因子为-1时,进行右左双旋。
5.当parent的平衡因子为2,parent的右孩子的平衡因子为1时,进行左单旋。
6.当parent的平衡因子为2,parent的右孩子的平衡因子为0时,也进行左单旋,但此时平衡因子调整与之前有所不同,具体看代码。

代码实现:

bool Erase(const K& key){//用于遍历二叉树Node* cur = parent;Node* parent = nullptr;//用于标记实际删除的节点和父节点Node* deletePos = nullptr;Node* deleteparentPos = nullptr;//确定删除节点while (cur) {if (key < cur->_kv.first) {parent = cur;cur = cur->_left;}else if (key > cur->_kv.first) {parent = cur;cur = cur->_right;}else//遇到了待删除的节点{if (cur->_left == nullptr)//如果待删除节点的左子树为空{if (cur == _root)//如果待删除节点是根节点{_root = cur->_right;if (_root) {_root->_parent = nullptr;}delete cur;return true;}else{deleteparentPos = parent;deletePos = cur;break;//有祖先节点说明要更新平衡因子}}else if (cur->_right == nullptr)//如果待删除节点的右子树为空{if (cur == _root)//如果待删除节点是根节点{_root = cur->_left;if (_root) {_root->_parent = nullptr;}delete cur;return true;}else{deleteparentPos = parent;deletePos = cur;break;//有祖先节点说明要更新平衡因子}}else//如果待删除节点的左右子树都不为空,替换法删除{//替换法删除//寻找待删除节点右子树中key值最小的节点作为实际删除节点(转化为左子树为空的局面)Node* minparent = cur;Node* minright = cur->_right;while (minright->_left){minparent = minright;minright = minright->_left;}//将待删除节点的属性更新为右子树中key值最小节点的属性cur->_kv.first = minright->_kv.first;cur->_kv.second = minright->_kv.second;//标记右子树中key值最小的节点为删除节点deleteparentPos = minparent;deletePos = minright;break;}}}//如果deletePos没有更新,说明没有找到根节点if (deletePos == nullptr){return false;}//记录待删除节点及其父节点(实际删除时要用)cur->_kv.first = minright->_kv.first;Node* del = deletePos;Node* delparent = deleteparentPos;//更新平衡因子while (deletePos != _root)//可能要一直更新到根节点{//判断删除的是父亲的哪一边,然后更新平衡因子if (deletePos = deleteparentPos->_left) {deleteparentPos->_bf++;}else{deleteparentPos->_bf--;}//判断是否需要往上继续更新if (abs(deleteparentPos->_bf) == 1) {break;}else if (deleteparentPos->_bf == 0) //其他两种情况树的高度都会变化所以要继续往上跟新平衡因子{deletePos = deleteparentPos;deleteparentPos = deleteparentPos->_parent;}else if (abs(deleteparentPos->_bf) == 2)//需要进行旋转处理{//判断旋转情况if (deleteparentPos->_bf = -2){if (deleteparentPos->_left->_bf == -1) {Node* tmp = deleteparentPos->_left;//记录右转后新的根节点rotateR(deleteparentPos);deleteparentPos = tmp;//旋转后根节点更新}else if(deleteparentPos->_left->_bf == 1) {Node* tmp = deleteparentPos->_left->_right;//记录右转后新的根节点rotateLR(deleteparentPos);deleteparentPos = tmp;//旋转后根节点更新}else//deleteparentPos->_left->_bf = 0{Node* tmp = deleteparentPos->_left;rotateR(deleteparentPos);deleteparentPos = tmp;deleteparentPos->_bf = 1;deleteparentPos->_right->_bf = -1;break;}}else//deleteparentPos->_bf = 2{if (deleteparentPos->_right->_bf == 1){Node* tmp = deleteparentPos->_right->_left;rotateRL(deleteparentPos);deleteparentPos = tmp;}else if (deleteparentPos->_right->_bf == -1){Node* tmp = deleteparentPos->_right;rotateL(deleteparentPos);deleteparentPos = tmp;}else{Node* tmp = deleteparentPos->_right;rotateL(deleteparentPos);deleteparentPos = tmp;deleteparentPos->_bf = -1;deleteparentPos->_left->_bf = 1;break;}}}else {assert(false);}//delParentPos树的高度变化,会影响其父结点的平衡因子,需要继续往上更新平衡因子deletePos = deleteparentPos;deleteparentPos = deleteparentPos->_parent;}if (del->_left == nullptr) {if (del = delparent->_left) {delparent->_left = del->_right;if (del->_right) {del->_right->_parent = delparent;}}else {delparent->_right = del->_right;if (del->_right) {del->_right->_parent = delparent;}}}else{if (del == delparent->_left) {delparent->_left = del->_left;if (del->_left) {del->_left->_parent = delparent;}}else {delparent->_right = del->_left;if (del->_left) {del->_left->_parent = delparent;}}}delete del;return true;}

AVL树的查找

Node* Find(const K& key){Node* cur = _root;while (cur){if (cur->_kv.first < key) // key值大于该结点的值{cur = cur->_left; // 在该结点的右子树当中查找}else if (cur->_kv.first < key) // key值小于该结点的值{cur = cur->_right; // 在该结点的左子树当中查找}else // 当前节点的值等于key值{return cur; //返回该结点}}return nullptr; //查找失败}

AVL树的高度

采用了分治的思想

private:
void _Height(Node* root){if (!root) {return 0;}int left = _Height(root->_left);int right = _Height(root->_right);return left > right ? left + 1 : right + 1;}
public:
void Height(){_Height(_root);}

AVL树的判定

	bool _IsBalancedTree(Node* root) {if (!root) {return true;}int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);int diff = rightHeight - leftHeight;if (abs(diff) > 2) {cout << "wrong" << endl;return false;}else if (diff != root->_bf) {return  false;}return _IsBalancedTree(root->_left) && _IsBalancedTree(root->_right);}

AVL树的遍历

void _Inorder(Node* root){if (!root) {return;}_Inorder(root->_left);cout << root->_kv.first << " ";_Inorder(root->_right);}

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

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

相关文章

vue2项目中表格的增删查改

我们在项目中经常会用到对于表格的增删查改操作&#xff0c;以下使用vue2elementui来实现表格的增删查改 表格的基本属性 基础表格如下:(其中需要注意的是当el-table元素中注入data对象数组后&#xff0c;在el-table-column中用prop属性来对应对象中的键名即可填入数据&#x…

Oracle创建控制列表ACL(Access Control List)

Oracle创建控制列表ACL&#xff08;Access Control List&#xff09; Oracle ACL简介一、先登陆163邮箱设置开启SMTP。二、Oracle ACL控制列表处理&#xff08;一&#xff09;创建ACL&#xff08;create_acl&#xff09;&#xff08;二&#xff09;添加ACL权限&#xff08;add_…

②matlab桌面和编辑器

目录 matlab编辑器练习 运行脚本 matlab编辑器练习 您可以通过点击灰色代码框在脚本中输入命令。 准备就绪后&#xff0c;您可以通过点击蓝色的提交按钮提交代码。 任务 在脚本中输入命令 r 3。 2.任务 在脚本中添加命令 x pi*r^2。 附加练习 当您在实时编辑器中完成…

MyBatis分页插件PageHelper的使用及MyBatis的特殊符号---详细介绍

一&#xff0c;分页的概念 分页是一种将大量数据或内容分割成多个页面以便逐页显示的方式。在分页中&#xff0c;数据被分割成一定数量的页&#xff0c;每页显示一部分数据或内容&#xff0c;用户可以通过翻页或跳分页是一种将大量数据或内容分割成多个页面以便逐页显示的方式。…

跨平台图表:ChartDirector for .NET 7.1 Crack

什么是新的 ChartDirector for .NET 7.0 支持跨平台使用&#xff0c;但仅限于 .NET 6。这是因为在 .NET 7 中&#xff0c;Microsoft 停止了用于非 Windows 使用的 .NET 图形库 System.Drawing.Common。由于 ChartDirector for .NET 7.0 依赖于该库&#xff0c;因此它不再支持 .…

设计模式—职责链模式(Chain of Responsibility)

目录 思维导图 什么是职责链模式&#xff1f; 有什么优点呢&#xff1f; 有什么缺点呢&#xff1f; 什么场景使用呢&#xff1f; 代码展示 ①、职责链模式 ②、加薪代码重构 思维导图 什么是职责链模式&#xff1f; 使多个对象都有机会处理请求&#xff0c;从而避免请…

视频汇聚/视频云存储/视频监控管理平台EasyCVR安全检查的相关问题及解决方法

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安…

与面试官互动:建立积极的技术讨论氛围

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

攻击与防御实战经验分享:分析真实的攻击事件和入侵行为,讨论防御方法和实践经验

章节 1: 前言 作为IT领域的从业者&#xff0c;我们时刻都面临着网络安全的挑战。攻击者不断寻找漏洞&#xff0c;而防御者则需要时刻保持警惕&#xff0c;采取最佳实践来保护系统和数据。在本文中&#xff0c;我们将分享一些真实的攻击事件和入侵行为&#xff0c;并探讨针对这…

API 接口应该如何设计?如何保证安全?如何签名?如何防重?

说明&#xff1a;在实际的业务中&#xff0c;难免会跟第三方系统进行数据的交互与传递&#xff0c;那么如何保证数据在传输过程中的安全呢&#xff08;防窃取&#xff09;&#xff1f;除了https的协议之外&#xff0c;能不能加上通用的一套算法以及规范来保证传输的安全性呢&am…

Eclipse打jar包与JavaDOC文档的生成

补充知识点——Eclipse打jar包与JavaDOC文档的生成 1、Eclipse如何打jar包&#xff0c;如何运行jar包 Java当中编写的Java代码&#xff0c;Java类、方法、接口这些东西就是项目中相关内容&#xff0c;到时候我们需要把代码提供给甲方、或者是我们需要运行我们编写的代码&…

Docker:Harbor 私有仓库迁移

Harbor 私有仓库迁移 一.私有仓库迁移的介绍 1.为何要对Harbor 私有仓库的迁移 &#xff08;1&#xff09;硬件升级或更换&#xff1a;如果源 Harbor 在旧的硬件设备上运行&#xff0c;并且计划将其迁移到新的硬件设备上&#xff0c;那么需要执行迁移操作。 &#xff08;2&…

Python爬虫追踪新闻事件发展进程及舆论反映

目录 实现方案 1. 确定目标新闻源&#xff1a; 2. 确定关键词&#xff1a; 3. 使用网络爬虫获取新闻内容&#xff1a; 4. 提取和分析新闻文章&#xff1a; 5. 追踪新闻事件的发展进程&#xff1a; 6. 监测舆论反映&#xff1a; 7. 数据可视化&#xff1a; 完整代码示例…

ExpressLRS开源之RC链路性能测试

ExpressLRS开源之RC链路性能测试 1. 源由2. 分析3. 测试方案4. 测试设计4.1 校准测试4.2 实验室测试4.3 拉距测试4.4 遮挡测试 5. 总结6. 参考资料 1. 源由 基于ExpressLRS开源基本调试验证方法&#xff0c;对RC链路性能进行简单的性能测试。 修改设计总能够满足合理的需求&a…

香橙派OrangePi zero H2+ 驱动移远4G/5G模块

目录 1 安装系统和内核文件&#xff1a; 1.1 下载镜像 1.2 内核头安装 1.2.1 下载内核 1.2.2 将内核头文件导入开发板中 1.2.3 安装内核头 2 安装依赖工具&#xff1a; 2.1 Installing Required Host Utilities 3 驱动步骤&#xff1a; 3.1 下载模块驱动文件…

Windows上安装Hadoop 3.x

目录 0. 安装Java 1. 安装Hadoop 1.1 下载Hadoop 1.2 下载winutils 2. 配置Hadoop 1. hadoop-env.cmd 2. 创建数据目录 3. core-site.xml 4. hdfs-site.xml 3. 启动测试 3.1 namenode格式化 3.2 启动Hadoop 3.3 查看webui 3.4 测试hdfs 3.5. 测试MapReduce 4. 还…

Markdown 扩展语法练习

风无痕 August 26, 2023 Markdown 指南中文版 Markdown 入门指南Markdown 基本语法Markdown 扩展语法Markdown 基本语法练习Markdown 扩展语法练习 代码 <h3 id"table">表格</h3>| Syntax | Description | | --- | --- | | Header | Title | | Paragrap…

【Terraform学习】使用 Terraform创建 S3 存储桶事件(Terraform-AWS最佳实战学习)

本站以分享各种运维经验和运维所需要的技能为主 《python》&#xff1a;python零基础入门学习 《shell》&#xff1a;shell学习 《terraform》持续更新中&#xff1a;terraform_Aws学习零基础入门到最佳实战 《k8》暂未更新 《docker学习》暂未更新 《ceph学习》ceph日常问题解…

用AI重构的钉钉,“钱”路在何方?

点击关注 文&#xff5c;郝 鑫&#xff0c;编&#xff5c;刘雨琦 钉钉2023年生态大会&#xff0c;离开了两年的无招&#xff0c;遇到了单飞9天的钉钉。 “做小钉钉、做好钉钉、做酷钉钉”&#xff0c;无招重申了钉钉的方向。 无招提到的三点&#xff0c;再加上“高质量增长”…

ubuntu22.04.1-live的vm虚拟机扩展磁盘

1、虚拟机分配硬盘100G&#xff0c;进系统df -h根目录只有50G 2、查看所有块设备 lsblk 3、 查看卷信息vgdisplay 4、在原有基础上增加49G lvextend -L 49G /dev/ubuntu-vg/ubuntu-lv 5、调整大小 resize2fs /dev/mapper/ubuntu--vg-ubuntu--lv