数据结构中的八大金刚--------八大排序算法

目录

引言

一:InsertSort(直接插入排序)

 二:ShellSort(希尔排序)

 三:BubbleSort(冒泡排序)

四: HeapSort(堆排序)

五:SelectSort(直接选择排序)

六:QuickSort(快速排序)

 1.Hoare版本

2.前后指针版本 

3.非递归版本 

4.快排之三路划分

5.SGI-IntrospectiveSort(自省排序)

七:MergeSort(归并排序) 

1.递归版本

2.非递归版本 

 八:CountSort(计数排序)

 

接下来的日子会顺顺利利,万事胜意,生活明朗-----------林辞忧 

引言

在日常生活当中任何地方都有着排序的思想,对于网购时价格排序,销量排序,好评排序等各种排名,因此对于学习排序算法是很重要,对于排序算法有常见的八种,它们分别是  InsertSort(直接插入排序)    ShellSort(希尔排序)    BubbleSort(冒泡排序)     HeapSort(堆排序)     SelectSort(直接选择排序)     QuickSort(快速排序)     MergeSort(归并排序)     CountSort(计数排序)   对于其他的如桶排序,奇数排序等实际用处不大,很少使用。接下来就介绍这八种排序算法

一:InsertSort(直接插入排序)

1.动图

2.思想:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列

void InsertSort(int* a, int n)
{//[0,end]有序,插入a[end+1]for (int i = 0; i < n - 1; ++i){int end=i;int tmp = a[end + 1];while (end>=0){if (a[end] > tmp){a[end + 1] = a[end];--end;}else{break;}}a[end + 1] = tmp;}
}

3.时间复杂度:O(N^2)    最坏情况是逆序,最好情况是有序或者接近有序O(N)

 二:ShellSort(希尔排序)

1.图片演示

2.思想:

先选定一个整数,把待排序文件中所有记录分成个 组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后,取,重复上述分组和排序的工 作。当到达 =1 时,所有记录在统一组内排好序
当gap>1时为预排序,目的是为了接近有序
当gap==1时为直接插入排序
间隔为gap的分为一组,总共gap组
先对一组进行排序
int gap = 3;
for (int i = 0; i < n - gap; i += gap)
{int end = i;int tmp = a[end + gap];while (end >= 0){if (a[end] > tmp){a[end + gap] = a[end];end -= gap;}else{break;}}a[end + gap] = tmp;
}

再对gap组都进行排序

int gap = 3;
for (int j = 0; j < gap; ++j)
{for (int i = j; i < n - gap; i += gap){int end = i;int tmp = a[end + gap];while (end >= 0){if (a[end] > tmp){a[end + gap] = a[end];end -= gap;}else{break;}}a[end + gap] = tmp;}
}

这样就完成了预排序,但如果对于数据量大的情况下,不止会进行一次预排序且还要控制最后一次预排序的gap==1这样就可以直接排序完成

int gap = n;
while (gap > 1)
{gap = gap / 3 + 1;for (int j = 0; j < gap; ++j){for (int i = j; i < n - gap; i += gap){int end = i;int tmp = a[end + gap];while (end >= 0){if (a[end] > tmp){a[end + gap] = a[end];end -= gap;}else{break;}}a[end + gap] = tmp;}}
}

对于这里可以优化掉一层循环变为

void ShellSort(int* a, int n)
{//当gap>1时为预排序,目的是为了接近有序//当gap==1时为直接插入排序//间隔为gap的分为一组,总共gap组int gap = n;while (gap > 1){gap = gap / 3 + 1;for (int i = 0; i < n - gap; ++i){int end = i;int tmp = a[end + gap];while (end >= 0){if (a[end] > tmp){a[end + gap] = a[end];end -= gap;}else{break;}}a[end + gap] = tmp;}}
}

以前的是一组排序完了再排下一组,这里是全部gap组一次排过去

3.关于gap如何取的问题以及一些其他注意问题

时间复杂度:O(N^1.3)

 三:BubbleSort(冒泡排序)

1.动图展示

2. 代码实现

void BubbleSort(int* a, int n)
{for (int i = 0; i < n - 1; ++i){int exchange = 0;for (int j = 0; j < n - 1 - i; ++j){if (a[j] > a[j + 1]){Swap(&a[j],&a[j+1]);exchange = 1;}}if (exchange == 0){break;}}
}

3.时间复杂度O(N^2) 

详细介绍可参考https://blog.csdn.net/Miwll/article/details/135315155?spm=1001.2014.3001.5501

四: HeapSort(堆排序)

1.图片展示

2.思想及代码实现

堆排序 (Heapsort) 是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。它是通过堆来进行选择数据。需要注意的是排升序要建大堆,排降序建小堆
void AdjustDown(int* a, int n, int parent)
{int child = parent * 2 + 1;while (child < n){if (child + 1 < n && a[child] < a[child + 1]){++child;}if (a[parent] < a[child]){Swap(&a[parent],&a[child]);parent = child;child = parent * 2 + 1;}else{break;}}
}
void HeapSort(int* a, int n)
{//排升序建大根堆--向下调整建堆for (int i = (n - 1 - 1) / 2; i >= 0; --i){AdjustDown(a, n, i);}//排数据--首尾交换再向下调整int end = n - 1;while (end){Swap(&a[end], &a[0]);AdjustDown(a, end, 0);--end;}
}

时间复杂度为O(N*logN) 

详细可以参考https://blog.csdn.net/Miwll/article/details/136636869?spm=1001.2014.3001.5501

五:SelectSort(直接选择排序)

1.动图显示

2.思想及实现

每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完 。
void SelectSort(int* a, int n)
{//遍历一遍选出最大和最小值下标,再收尾交换int begin = 0, end = n - 1;int maxi = 0, mini = 0;while (begin < end){for (int i = begin; i <= end; ++i){if (a[i] > a[maxi]) maxi = i;if (a[i] < a[mini]) mini = i;}Swap(&a[begin], &a[mini]);if (begin == maxi){maxi = mini;}Swap(&a[end], &a[maxi]);begin++;end--;}
}

这里需要注意当begin和maxi重叠的情况

时间复杂度:O(N^2)

六:QuickSort(快速排序)

 1.Hoare版本

1.动图展示

2.思想及实现

对于Hoare版本的快排是选取一个key值,然后先让右边先走找小,再左边找大,再交换继续往后直至相遇,再交换key位置处的值,再以相遇位置为划分子区间继续执行

画一部分递归展开图理解最小子问题的条件

 

3.为啥相遇位置一定比key值小?---右边先走保证的

 

4.快排的时间复杂度为O(N*logN),但在有序或者接近有序的情况下最坏为O(N^2),为了防止出现最坏的情况,可以使用三数取中或者随机选key来解决问题

//2.三数取中
int mid = GetMidi(a,begin, end);
Swap(&a[begin], &a[mid]);int GetMidi(int*a,int left, int right)
{int mid = (left + right) / 2;if (a[left] < a[mid]){if (a[right] > a[mid])//mid>left right>mid{return mid;}else if (a[left] > a[right])//mid>left right<mid{return left;}else{return right;}}else //left>mid{if (a[right] < a[mid])//right<mid{return mid;}else if (a[left] > a[right])//right>mid {return right;}else{return left;}}
}

5.小区间优化  由于到最后的几步时,递归的深度和广度是非常巨大的,因此可以采用小区间优化的方式减少递归,这里可以采用插入排序

void QuickSort1(int* a, int begin, int end)
{//最小子问题--区间不存在或者只有一个数据if (begin >= end) return;//1.随机选key---选[left, right]区间中的随机数做key//int randi = rand() % (end - begin + 1);//randi += begin;//在递归时begin不一定是0开始的//Swap(&a[begin],&a[randi]);if (end - begin + 1 < 10){InsertSort(a+begin, end - begin + 1);}else{//2.三数取中int mid = GetMidi(a, begin, end);Swap(&a[begin], &a[mid]);int keyi = begin;int left = begin, right = end;while (left < right)//相遇时就停止{//先让右边走while (left < right && a[right] >= a[keyi]){--right;}//再左边走while (left < right && a[left] <= a[keyi]){++left;}Swap(&a[left], &a[right]);}Swap(&a[keyi], &a[left]);keyi = left;//[begin,keyi-1] keyi [keyi+1,end]QuickSort1(a, begin, keyi - 1);QuickSort1(a, keyi + 1, end);}
}

2.前后指针版本 

 1.动图展示

2.思想及实现

当cur处的值>=key时,++cur

当cur处的值<key时,++prev,再交换prev和cur的值,再++cur

void QuickSort2(int* a, int begin, int end)
{if (begin >= end) return;int prev = begin;int cur = begin + 1;int keyi = begin;while (cur <= end){if (a[cur] < a[keyi] && ++prev != cur){Swap(&a[prev],&a[cur]);}++cur;}Swap(&a[prev],&a[keyi]);keyi = prev;//[begin,keyi-1]keyi[keyi+1,end]QuickSort2(a, begin, keyi - 1);QuickSort2(a, keyi + 1, end);
}

3.非递归版本 

对于递归如果深度太深的话,就会导致栈溢出,因此用栈实现非递归版本很重要

思想:走一趟单趟,再右左区间入栈

void QuickSortNonR(int* a, int begin, int end)
{ST st;STInit(&st);//先入右再入左STPush(&st, end);STPush(&st, begin);while (!STEmpty(&st)){int left = STTop(&st);STPop(&st);int right = STTop(&st);STPop(&st);//单趟int keyi = left;int cur = left + 1;int prev = left;while (cur <= right){if (a[cur] < a[keyi] && ++prev != cur)Swap(&a[cur],&a[prev]);++cur;}Swap(&a[prev], &a[keyi]);keyi = prev;//[left,keyi-1]keyi[keyi+1,right]//保证入的区间有效if (keyi + 1 < right){STPush(&st, right);STPush(&st, keyi + 1);}if (left < keyi -1){STPush(&st, keyi - 1);STPush(&st, left);}//Print(a, left, right);}STDestroy(&st);
}

4.快排之三路划分

1.快排性能的关键点分析

决定快排性能的关键点是每次单趟排序后,key对数组的分割,如果每次选key基本⼆分居中,那么快 排的递归树就是颗均匀的满⼆叉树,性能最佳。但是实践中虽然不可能每次都是⼆分居中,但是性能 也还是可控的。但是如果出现每次选到最⼩值/最⼤值,划分为0个和N-1的⼦问题时,时间复杂度为 O(N^2),数组序列有序时就会出现这样的问题,我们前⾯已经⽤三数取中或者随机选key解决了这个问 题,也就是说我们解决了绝⼤多数的问题,但是现在还是有⼀些场景没解决(数组中有⼤量重复数据时),即以下情况

 此时就提出了采用三路划分的思想来解决

这样再对比key大的数据区间和比key小的数据区间进行递归

//三路划分
void QuickSort3(int* a, int begin, int end)
{//最小子问题if (begin >= end) return;int left = begin, right = end;int key = a[left];int cur = left + 1;while (cur <= right){if (a[cur] < key){Swap(&a[left], &a[cur]);++cur;++left;}else if(a[cur]>key){Swap(&a[cur],&a[right]);--right;}else{++cur;}}//[begin,left-1][left,right][right+1,end]QuickSort3(a, begin, left - 1);QuickSort3(a, right+1, end);
}

5.SGI-IntrospectiveSort(自省排序)

 introsort是introspectivesort采⽤了缩写,他的名字其实表达了他的实现思路,他的思路就是进⾏⾃ 我侦测和反省,快排递归深度太深(sgistl中使⽤的是深度为2倍排序元素数量的对数值)那就说明在 这种数据序列下,选key出现了问题,性能在快速退化,那么就不要再进⾏快排分割递归了,改换为堆 排序进⾏排序

void IntroSort(int* a, int left, int right, int depth, int defaultDepth)
{if (left >= right)return;// 数组长度⼩于16的⼩数组,换为插入排序,简单递归次数---小区间优化    if (right - left + 1 < 16){InsertSort(a + left, right - left + 1);return;}// 当深度超过2 * logN时改用堆排序    if (depth > defaultDepth){HeapSort(a + left, right - left + 1);return;}depth++;int begin = left;int end = right;// 随机选keyint randi = left + (rand() % (right - left));Swap(&a[left], &a[randi]);int prev = left;int cur = prev + 1;int keyi = left;while (cur <= right){if (a[cur] < a[keyi] && ++prev != cur){Swap(&a[prev], &a[cur]);}++cur;}Swap(&a[prev], &a[keyi]);keyi = prev;// [begin, keyi-1] keyi [keyi+1, end]IntroSort(a, begin, keyi - 1, depth, defaultDepth);IntroSort(a, keyi + 1, end, depth, defaultDepth);
}void QuickSort4(int* a, int begin, int end)
{int depth = 0;int logn = 0;int N= end - begin + 1;for (int i = 1; i < N; i *= 2){logn++;}// introspective sort -- 自省排序IntroSort(a, begin, end, depth, logn * 2);
}

七:MergeSort(归并排序) 

1.递归版本

1.动图演示

 2.思想及实现

归并排序( MERGE-SORT )是建立在归并操作上的一种有效的排序算法 , 该算法是采用分治法 Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有 序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并

void _MergeSort(int* a, int left,int right,int*tmp)
{//最小子问题if (left == right) return;int mid = (left + right) / 2;//[begin,mid][mid+1,end]_MergeSort(a, left, mid, tmp);_MergeSort(a, mid + 1, right, tmp);//开始归并int begin1 = left, end1=mid;int begin2=mid+1, end2=right;int i = left;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] <= a[begin2]){tmp[i++] = a[begin1++];}else{tmp[i++] = a[begin2++];}}while (begin1 <= end1){tmp[i++] = a[begin1++];}while (begin2 <= end2){tmp[i++] = a[begin2++];}memcpy(a + left, tmp + left, sizeof(int) * (right-left+1));
}
void MergeSort(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc fail\n");return;}_MergeSort(a, 0, n - 1, tmp);free(tmp);tmp = NULL;
}

 部分递归展开

2.非递归版本 

这里的非递归版本采用循环的方式来解决

void MergeSortNonR(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc fail\n");return;}int gap = 1;while (gap < n){for (int i = 0; i < n; i += 2 * gap){int begin1 = i, end1 = begin1 + gap - 1;int begin2 = end1 + 1, end2 = begin2 + gap - 1;//调整越界问题if (end1 >= n || begin2 >= n){break;}if (end2 >= n)//修正{end2 = n - 1;}int j = i;while (begin1 <= end1 && begin2 <= end2){//取小的尾插if (a[begin1] <= a[begin2]){tmp[j++] = a[begin1++];}else{tmp[j++] = a[begin2++];}}while (begin1 <= end1){tmp[j++] = a[begin1++];}while (begin2 <= end2){tmp[j++] = a[begin2++];}memcpy(a + i, tmp + i, sizeof(int) * (end2-i+1));}gap *= 2;}
}

 八:CountSort(计数排序)

1.思想及实现

开辟一个数组用来统计每个数据出现的次数,在相对映射位置的次数++,然后再往原数组写入数据,适合于整形且数据集中的

void CountSort(int* a, int n)
{int min = a[0];int max = a[0];for (int i = 1; i < n; ++i){if (min > a[i]) min = a[i];if (max < a[i]) max = a[i];}int range = max - min + 1;int* count = (int*)malloc(sizeof(int) * range);if (count == NULL){perror("malloc fail\n");return;}memset(count, 0, sizeof(int) * range);//统计次数for (int i = 0; i < n; ++i){count[a[i] - min]++;}//写回原数组int j = 0;for (int i = 0; i < range; ++i){while (count[i]--){a[j++] = i + min;}}
}

 总结

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

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

相关文章

【Hot100】LeetCode—416. 分割等和子集

目录 题目1- 思路2- 实现⭐152. 乘积最大子数组——题解思路 3- ACM 实现 题目 原题连接&#xff1a;416. 分割等和子集 1- 思路 理解为背包问题 思路&#xff1a; 能否将均分的子集理解为一个背包&#xff0c;比如对于 [1,5,11,5]&#xff0c;判断能否凑齐背包为 11 的容量…

Linux云计算 |【第二阶段】AUTOMATION-DAY2

主要内容&#xff1a; 部署GitLab、配置管理GitLab、CI/CD概述、Jenkins概述、部署Jenkins&#xff08;初始化、拷贝插件&#xff09; 一、GitLab概述 GitLab 是一个基于 Web 的 Git 仓库管理工具&#xff0c;它提供了一个集成的开发环境和代码管理平台。GitLab 不仅支持 Git…

便携式自动气象站:科技赋能气象观测

便携式自动气象站&#xff0c;顾名思义&#xff0c;就是一款集成了多种气象传感器&#xff0c;能够自动进行气象观测和数据记录的设备。它体积小巧、重量轻&#xff0c;便于携带和快速部署&#xff0c;可以在各种环境下进行气象数据的实时监测。同时&#xff0c;通过内置的无线…

keil单步调试需要点击多次

问题描述&#xff1a;在进行调试的时候&#xff0c;点击单步调试&#xff0c;总是需要点击好几次才可以运行到下一条语句。 我点击单步调试的时候&#xff0c;会在红色框内进行单步运行&#xff0c;也就是在汇编代码内单步执行。 解决办法&#xff1a; 关闭汇编窗口 就是这个小…

案例研究|柯尼卡美能达软件开发(大连)有限公司基于DataEase构筑内部数据可视化体系

柯尼卡美能达软件开发&#xff08;大连&#xff09;有限公司于2007年5月25日注册成立。公司以“洞悉在工作的人们真实情况&#xff0c;探寻他们的愿望&#xff0c;持续提供使人们更加幸福的服务”为使命&#xff0c;致力于系统品质测试服务、软件开发服务、IT安全服务、高级BPO…

SpringBoot缓存注解使用

背景 除了 RedisTemplate 外&#xff0c; 自Spring3.1开始&#xff0c;Spring自带了对缓存的支持。我们可以直接使用Spring缓存技术将某些数据放入本机的缓存中&#xff1b;Spring缓存技术也可以搭配其他缓存中间件(如Redis等)进行使用&#xff0c;将某些数据写入到缓存中间件…

Golang | Leetcode Golang题解之第264题丑数II

题目&#xff1a; 题解&#xff1a; func nthUglyNumber(n int) int {dp : make([]int, n1)dp[1] 1p2, p3, p5 : 1, 1, 1for i : 2; i < n; i {x2, x3, x5 : dp[p2]*2, dp[p3]*3, dp[p5]*5dp[i] min(min(x2, x3), x5)if dp[i] x2 {p2}if dp[i] x3 {p3}if dp[i] x5 {p5…

Xcode学习笔记

Xcode学习笔记 前言一、在Mac上安装Xcode并做点简单设置1.查看一下Xcode的版本 二、使用Xcode新建一个Playground三、swift基础-变量1.swift是什么2.变量是什么3.建立变量4.改变变量5.小帖士 四、swift基础-变量命名规范1.使用小驼峰命名法2.使用有意义且描述性的名称3.避免使用…

《云原生安全攻防》-- 容器攻击案例:镜像投毒与Fork炸弹

在本节课程中&#xff0c;我们将介绍两个比较有意思的容器攻击案例&#xff0c;镜像投毒与Fork炸弹。 在这个课程中&#xff0c;我们将学习以下内容&#xff1a; 镜像投毒&#xff1a;构建恶意镜像&#xff0c;诱导用户拉取镜像创建容器。 Fork炸弹&#xff1a;Fork炸弹的攻击…

Mysql基础速过

一、数据库相关概念 1.1 相关概念 名称全称简称数据库存储数据的仓库、数据是有组织的进行存储DataBase(DB)数据库管理系统操作和管理数据库的大型软件DataBase Management System(DBMS)SQL操作关系型数据库的编程语言&#xff0c;定义了一套操作关系型数据库统一标准Structu…

【iOS】Tagged Pointer

目录 前言什么是Tagged Pointer&#xff1f;引入Tagged Pointer技术之前引入Tagged Pointer之后总结 Tagged Pointer原理&#xff08;TagData分析&#xff09;关闭数据混淆MacOS分析NSNumberNSString iOS分析 判断Tagged PointerTagged Pointer应用Tagged Pointer 注意点 Tagge…

科研绘图系列:R语言微生物堆积图(stacked barplot)

介绍 堆叠条形图是一种数据可视化图表,它通过将每个条形分割成多个部分来展示不同类别的数值。每个条形代表一个总体数据,而条形内的每个部分则代表该总体数据中不同子类别的数值。这种图表特别适合展示整体与部分的关系,以及各部分在整体中的比例。 特点: 多部分条形:每…

Python爬虫:代理ip电商数据实战

引言&#xff1a;数据访问管理引发的烦恼 作为一名Python博主&#xff0c;爬虫技能对于获取和分析数据至关重要&#xff0c;经常爬一下&#xff0c;有益身心健康嘛。爬虫技术对很多人来说&#xff0c;不仅仅是一种工具&#xff0c;更像是一种艺术&#xff0c;帮助我们从互联网…

工程视角:数据结构驱动的应用开发--字典(dictionary),列表(list)与实体

这里写目录标题 业务业务场景流程分析 实现数据访问层&#xff08;DAL&#xff09;业务逻辑层&#xff08;BLL&#xff09;用户界面层&#xff08;UI&#xff09;工具类 设计思路为什么抽出工具类关于U层使用字典的好处工程视角 业务 业务场景 在一个金融应用系统中&#xff0c…

【益智问题】十三球称重

十三球称重 问题描述解决方案 问题描述 有13个外表一模一样的小球产品&#xff0c;其中有一个是次品&#xff0c;其质量与其他12个略有差别&#xff0c;但不知道是重还是轻。这13个球上都有标号&#xff0c;现在给你-架天平&#xff0c;只能使用三次&#xff0c;把这个次品小球…

Elasticsearch:Golang ECS 日志记录 - zap

ECS 记录器是你最喜欢的日志库的格式化程序/编码器插件。它们可让你轻松地将日志格式化为与 ECS 兼容的 JSON。 编码器以 JSON 格式记录日志&#xff0c;并在可能的情况下依赖默认的 zapcore/json_encoder。它还处理 ECS 错误格式的错误字段记录。 默认情况下&#xff0c;会添…

58、主从复制数据库+读写分离

mysql的主从复制和读写分离&#xff08;面试问原理&#xff09; mysql的主从复制和读写分离&#xff1a; 主从复制 面试必问 主从复制的原理。 读写分离&#xff0c;MHA 一、主从复制 1.1、主从复制的模式&#xff1a; 1、mysql的默认模式&#xff1a; 异步模式&#xf…

Three.js 官方文档学习笔记

Address&#xff1a;Three.js中文网 (webgl3d.cn) Author&#xff1a;方越 50041588 Date&#xff1a;2024-07-19 第一个3D案例—创建3D场景 创建3D场景对象Scene&#xff1a; const scene new THREE.Scene(); 创建一个长方体几何对象Geometry&#xff1a; const geomet…

FTP、NFS、SAMBA系统服务

⼀、rsync托管xinetd 1 、为什么要进⾏服务托管 独⽴服务&#xff1a;独⽴启动脚本 ssh ftp nfs dns ... 依赖服务 : 没有独⽴的启动脚本 rsync telnet 依赖 xinetd 服务&#xff08;独⽴服务&#xff09; 2 、如何将 rsync 托管给 xinetd 服务去管理&#xff1f; 第⼀步&am…

VMware中Ubuntu磁盘空间的清理

最近发现Ubuntu占用空间过大&#xff0c;在网上找了一些方法&#xff0c;在这里总结一下。 1.删除快照 把不需要的快照删除&#xff0c;但要注意删除快照可能会影响到后续的快照链。每个快照依赖于前面的快照。如果删除一个中间快照&#xff0c;虚拟机可能无法找到完整的差异…