算法设计与分析 实验一 排序算法性能分析

  • 算法实验不提供代码与实验数据,仅提供思路
      

一、实验目的与要求

  1. 掌握选择排序、冒泡排序、合并排序、快速排序、插入排序算法原理
  2. 掌握不同排序算法时间效率的经验分析方法,验证理论分析与经验分析的一致性。

  

二、实验原理

1、选择排序

1. 算法思路

  1. 如图 1所示,选择排序不断遍历待排序列,每轮选择出一个最小(或最大)的元素,与待排序列首部元素交换位置,此时该元素被放至正确位置。
  2. 每经过一轮遍历,则保证有一个元素被放置至序列正确位置。使得已排序序列长度+1,待排序序列长度-1(迭代)。
  3. 不断重复步骤1,直至所有元素排序完毕。至多经过n-1次循环,即可完成n个元素序列的排序。
    在这里插入图片描述

2. 伪代码

  1. 选择排序算法伪代码(以升序序列为例)如图 2所示;
  2. 对于每一轮外循环来说,Array[0…i-1]为有序序列,Array[i…n-1]为待排序序列;
  3. 因此选择排序的外层循环i表示当前有序序列长度和位置,而内层循环j由于找到待排序序列最小元素位置,并将该元素移至有序序列末尾(即无序序列首部);
  4. 每一轮循环都能使有序序列新增一个元素,故当i=n-1时,有序序列长度为n,循环结束。数组完成排序。
    在这里插入图片描述

3. 性能分析

时间复杂度:选择排序使用了双循环结构,整个流程要执行n-1轮选择和n-1次交换,每轮选择要比较n-i次,共进行了n*(n-1)/2次比较,故时间复杂度为O(n^2)。

空间复杂度: 选择排序只需开辟常数个变量进行存储下标和交换元素,故空间复杂度为O(1)。

2、冒泡排序

1. 算法思路

  1. 如图 3所示,冒泡排序不断遍历待排序列,将待排序列相邻元素依次比较,若两个元素间的顺序不符合序列要求则交换位置,直至序列末尾。
  2. 则经过一轮遍历,当前序列的最大元素会被一直交换至待排序列末尾。从而使有序序列长度+1,待排序序列长度-1(迭代)。
  3. 重复步骤1,不断遍历待排序列,直至所有元素排序完成。至多经过n-1轮循环,长度为n的序列排序完成。
    在这里插入图片描述

2. 伪代码

  1. 冒泡排序算法伪代码(以升序序列为例) 如图 4所示;
  2. 对于每轮循环来说,Array[0…n-i-1]为待排序序列,Array[n-i,n-1]为有序序列;
  3. 不断比较无序序列中的相邻元素,若当前元素大于下一个位置元素,则进行元素交换。从而使最大的元素不断被交换直至序列末尾。
  4. 每一轮循环都保证至少有一个元素被放至正确位置,故经过n-1轮循环后,有序序列长度为n,数组完成排序。
    在这里插入图片描述

3. 性能分析

时间复杂度:冒泡排序使用了双循环结构,整个流程要执行n-1轮冒泡,每轮冒泡要比较n-i次元素,共进行了n*(n-1)/2次比较,故时间复杂度为O(n^2)。

空间复杂度: 冒泡排序只需开辟常数个变量进行交换元素,故空间复杂度为O(1)。

与选择排序相比,冒泡排序在元素比较时就需要进行元素交换,而选择排序每轮循环只需交换一次元素。因此普遍情况下,冒泡排序执行交换次数更多,性能表现也更差。

3、合并排序

1. 算法思路

  1. 归并算法是建立在归并操作上的一种算法,其原理是将序列进行多次折半分组,然后对每个分组进行组内排序。排序完成后,将分组两两合并为一个更大的分组,重复排序合并操作,直至所有分组合成一个,排序完成。
  2. 如图 5所示,归并排序将序列逐层折半分组,排序完成后再逐层合并,最终得到排序序列。
    在这里插入图片描述

2. 伪代码

  1. 归并排序伪代码如图 6所示,该算法使用了递归实现;
  2. 归并排序由两部分代码实现,分别是MergeSort与Merge部分。
  3. MergeSort函数是归并排序的递归调用部分,这部分不断地将数组进行折半分组,直至不可再分,并将小分组两两进行排序与合并。
  4. Merge函数负责将两个小分组进行合并与排序。合并的前提是小分组内有序,由于递归的设计,每一层递归后,上一层的小分组组内总是有序的。
  5. 合并时,只需创建一个大小为两组大小之和的数组,不断按两小分组内元素的大小进行插入即可。最终结果是将两个有序的小分组合并为有序的大分组。
    在这里插入图片描述
    在这里插入图片描述

3. 性能分析

时间复杂度:归并排序要将长度为n的序列不断折半分组,直至不可再分,分组时间复杂度为O(log n)。分组后要不断进行组间合并与排序,由于排序是线性插入的方式,故合并与排序的过程时间复杂度为O(n)。因此,归并排序平均时间复杂度为O(nlog n)。

空间复杂度: 归并排序需要创建临时数组存储合并后的分组,故空间复杂度为O(n)。

4、快速排序

1. 算法思路

  1. 快速排序和归并排序都是基于分治思想的排序算法,其思路是从待排序列选出一个数为基准元素(通常为首元素)。
  2. 然后将序列中小于基准元素的元素放至序列左边,大于基准元素的元素放至序列右边,分界部分则插入基准元素,此时基准元素就被放在了待排序列的正确位置。
  3. 对基准元素左侧子序列和右侧子序列递归进行快速排序,直到子序列长度为1时停止,此时快速排序完成,如图 7所示。
    在这里插入图片描述

2. 伪代码

  1. 快速排序伪代码如图 8所示,该算法使用了递归实现。
  2. 代码由两部分组成,分别是QuickSort部分和Partition部分。
    2.1 QuickSort部分首先获得本轮快排成功元素的位置,再将该元素的左子序列与右子序列进行快排,形成递归。
    2.2 Partition部分负责将传入序列的首元素放至合适的位置。首先将首元素值存为基准元素,然后设置两个哨兵(头哨与尾哨)。
  3. 由尾哨开始从后往前找到第一个小于基准的元素位置,与头哨元素交换,继而头哨从前往后找到第一个大于基准的元素位置,与尾哨元素交换。头哨与尾哨交替进行,直至两个哨兵相遇。
  4. 此时这个位置的前方元素均小于基准值,后方元素均大于基准值,再将基准元素放至这个位置,本轮排序完成。
    在这里插入图片描述
    在这里插入图片描述

3. 性能分析

时间复杂度:快速排序的运行时间与每轮排序子序列的划分有关,最坏的情况下,基准元素总是在序列的两端,子序列分割不平衡,增加递归深度,使算法时间复杂度退化为O(N^2)。但在情况一般下,子序列划分总是比较平均,因此平均时间复杂度为O(nlog n)。

空间复杂度: 快速排序的额外空间开销与递归深度有关,平均情况下要将区间划分log n次,每个划分都要开销一个位置存储pivot元素,因此平均空间复杂度为O(log n)。

5、插入排序

1. 算法思路

  插入排序的算法思路非常简单,每轮将待排序列的第一个元素插入到前面的有序序列中,直至所有元素被插入至有序序列。如图 9所示。

在这里插入图片描述

2. 伪代码

  1. 插入排序伪代码如图 10所示;
  2. 设长度为n的目标序列被分成两个子序列,其中A[1…i]为有序序列,A[i+1…n]为待排序列;
  3. 对于每趟排序,取出待排序列的第一个元素pivot,从后往前访问有序序列,并不断与其元素进行比较,当找到第一个比pivot小的元素a时,将a后方元素全部后移一位,从而空出位置使pivot在a后方插入;
  4. 每经过一轮循环,则有一个待排序列元素被插入至有序序列,经过n-1次插入后,排序完成。
    在这里插入图片描述

3. 性能分析

时间复杂度:插入排序在排序过程中,要不断向有序序列插入元素,这个过程进行n-1次。每次插入都要进行元素比较与元素后移,这个过程的时间复杂度为O(n)。故插入排序的平均时间复杂度为O(n^2)。

空间复杂度: 插入排序比较与插入后移过程都是在原序列上进行的,只需额外开辟一个临时变量暂存元素,故空间复杂度为O(1)。
  

三、实验步骤

  1. 实验以以待排序数组的大小n为输入规模,固定n,随机产生20组测试样本,统计不同排序算法在20个样本上的平均运行时间;分别以n=10, n=100, n=1000, n=10000, n=100000,n=1000000,重复实验。
  2. 每次实验以时间为种子,随机生成数组,随机数范围与数组规模相当。
  3. 用excel记录数据,绘制规模与运行时间关系图。以n=100000为基准,推测其他规模的运行时间,比进行比较。

  

四、五种排序的对比

1、总体对比

  理论上讲,插入排序、选择排序、冒泡排序都属于时间复杂度为O(n^2)的算法,而快速排序、归并排序都属于时间复杂度为O(n logn)的算法,因此在较大规模的数据集时,两类算法的时间增长差异显著。

在这里插入图片描述

  可以看出,在n<100这种小规模数据集条件下,归并和快速排序这类nlogn算法效率不如n2类算法,原因是这种条件下,递归、函数调用带来的额外开销过大,占比过高,因此实际性能不如实现更为简单的n2类算法。
  实际上,无论是哪类算法,在极小规模的数据集情况下,排序时间都极短。

在这里插入图片描述

  当规模逐渐增大至n>100时,nlogn类算法优势极其明显。由于纵坐标是平均时间对数,因此表现出来的时间差距是指数级差距。
  当n=1000000的大规模数据集时,两类排序的平均时间相差4个数量级,这个差距会随着规模增大而继续以指数形式增大。

在这里插入图片描述

  这两个结果符合我们对这五种算法(两类)时间复杂度的预期,也说明面对不同规模的数据集排序任务,选取合适的算法对提高效率有显著影响:
  对于小规模数据集,选择插入排序这类n^2复杂度算法即高效又简单;
  但对于大规模数据集,选择归并排序或选择排序这类nlogn复杂度算法能非常显著的提高效率,能让我们的排序时间缩小几个数量级。
  

五、实验结论与体会

总结:

  1. 面对不同规模的数据集排序任务和排序要求,选取合适的算法对提高效率有显著影响:
  2. 仅考虑时间效率,对于小规模数据集,选择插入排序这类n^2复杂度算法即高效又简单,而nlogn类算法反而由于递归、数组等额外开销导致性能更低。
  3. 但对于大规模数据集,选择归并排序或选择排序这类nlogn复杂度算法能非常显著的提高效率。

  事实上,不同的算法有不同的实现原理,适用于不同场景,具体使用哪种算法,我们不能一概而论。除了时间复杂度以外,空间复杂度、稳定性要求、数据分布都是我们需要权衡的因素,我们应该根据实际需求与条件选择适合的算法,才能更好的提高工作效率。

  

六、算法改进

1、NLOGN类算法在小规模数据时效率不高。

解决方案: 在排序前进行规模判断,若规模过小,则切换为插入排序等算法。

2、面对大规模数据集,NLOGN类算法虽然效率很高,但是递归深度和额外空间开辟过大,容易导致栈溢出。

解决方案:
① 限制递归深度,当递归到一定深度,就将当前子序列转为其他非递归方法排序。
② 进行三路快排,除了大于和小于基准部分,还有等于基准部分,这部分不参与递归,减少递归次数。

3、快速排序效率容易受序列有序性影响,子序列分割不平衡,增加递归深度,使算法时间复杂度退化为O(N^2)。

解决方案: 随机化选择基准值,避免基准值总是落在序列两端,影响子序列划分。

4、超大规模数据集排序。

解决方案:
1、将数据集分块存储在不同主机上。
2、每个主机都将自己的数据进行排序。得到一群有序小数据集。
3、模拟归并操作,每个主机都输出当前存储的最小值,进行比较。归并结果按主机编号分块有序存储到不同主机上。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值