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

简介: 算法设计与分析 实验一 排序算法性能分析(中)

5. 合并排序


(1)算法实现原理


合并排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。

将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。合并排序也叫归并排序。

下面以使用合并排序14,12,15,13,11,16这六个数字为例,以图示介绍合并排序,见下图

0d0827cae2484c8a849ee18332d997fe.png


(2)源代码

//2、合并化
MERGE(sourceArr,tempArr,sIndex,midIndex,eIndex)
    i = sIndex
    j = midIndex+1
    k = sIndex
//取出两个有序子序列中最小的一个元素,放入新的有序数列中
    while i! = midIndex+1 and j!=eIndex+1
        if sourceArr[i] < sourceArr[j]
            tempArr[k] = sourceArr[i]
            i++
        else
            tempArr[k] = sourceArr[j]
            j++
        k++
//前面的有序数列中还有元素
    while i != midIndex+1
        tempArr[k++] = sourceArr[i++]
//后面的有序数列还有元素
    while j != eIndex+1
        tempArr[k++] = sourceArr[j++]
//将有序数列拷贝给原数列
    for m = sIndex to eIndex
        sourceArr[m] = temp[m]
//1、归一化
MERGESORT(sourceArr,tempArr,sIndex,eIndex)
    //类似二分查找一样,每次取半
    if sIndex < eIndex
       mid = sIndex + (eIndex - sIndex)/2
       MERGESORT(sourceArr,tempArr,sIndex,mid)
       MERGESORT(sourceArr,tempArr,mid+1,eIndex)
       MERGE(sourceArr,tempArr,sIndex,mid,eIndex)

3)算法性能分析


①时间复杂度分析:

不妨假设一个序列有n nn个数的排序时间为T ( n ),T ( n )是一个关于n 的函数,随着n 的变化而变化。

那么我们将n 个数的序列,分为两个0061aa5e6f1240b2af127d8165c43f88.png的序列。则有:

image.png


由于合并时,两个子序列已经组内排好序了,那我们将两个排好序的序列组合成一个大的有序序列,只需要一个if循环即可。if循环中有n个数需要比较,所以时间复杂度为n。则有:


image.png


②数据测试分析

使用随机数生成器生成了从101106的不同数据量级的随机数,通过使用合并排序进行排序并获取了程序运行时间。为降低偶然性,对20组数据取平均值,选择105时运行时间为理论值并做表如下

afdea92239cb40d396a463005c79d4d4.png

通过获得的数据,使用106 时的时间消耗为基准理论值,使用Tableau做图像并拟合如图:


3f062fd33c2f4ee882f020bba5ba9e83.png

结合以上表以及上图不难得出:整体时间消耗都大致满足O ( n log ⁡ n ) 的时间复杂度。但对于101102两组数据存在较大偏差。这是因为合并排序需要额外的临时空间辅助,有一定的资源损耗,且当数据量较小时,资源损耗的影响将变得显著。从一定程度上导致,实际时间会大于理论时间。当数据量较大且运行时间变长时,拟合效果比较好。


6. 基数排序


(1)算法实现原理

①将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。

②然后,从最低位开始,进行一次排序。

③从第二低位进行一次排序

④依次按照以上排序方法从最低位一直到最高位排序完成后, 数列就变成有序序列。


下面以使用基数排序123,156,945,624,464,32这六个数字为例,以图示介绍基数排序,见下图

a9da15289f424c16b8540ce7ac74d397.png

(2)源代码


RADIXSORT(A,d)
  for i = 1 to d
    use a stable sort to sort array A on digit i


(3)算法性能分析


①时间复杂度分析:

基数排序的时间复杂度是O ( k ⋅ n ),其中n是排序元素个数,k kk是数字位数。k kk的大小取决于数字位的选择(比如比特位数),和待排序数据所属数据类型的全集的大小; k 决定了进行多少轮处理,而n nn是每轮处理的操作数目。

以排序n nn个不同整数来举例,假定这些整数以B 为底,这样每位数都有B 个不同的数字,k = log ⁡ B ⁡ N,N待排序数据类型全集的势。虽然有B 个不同的数字,需要B 个不同的桶,但在每一轮处理中,判断每个待排序数据项只需要一次计算确定对应数位的值,因此在每一轮处理的时候都需要平均n 次操作来把整数放到合适的桶中去,所以就有:


c3a2e6c5024f4b3bba0167b62f4481e0.png

②数据测试分析

使用随机数生成器生成了从 101106的不同数据量级的随机数,通过使用基数排序进行排序并获取了程序运行时间。为降低偶然性,对20组数据取平均值,选择105时运行时间为理论值并做表如下:

a6709d5198bb44beba3a339c85fbf627.png

通过获得的数据,使用 105时的时间消耗为基准理论值,使用Tableau做图像并拟合如图

0df4ad4232604e23ae395056a3f740ce.png

结合上图表不难得出:整体时间消耗都大致满足O ( n log ⁡ ⁡ n )的时间复杂度。但对于

两组数据存在较大偏差。这是因为当数据量较小时,基数排序对较少位数进行排序时内,101102存消耗较大,比较交换次数多,且需要对每一基数进行收集排序,一定程度上导致了时间的增加。当数据量较大且运行时间变长时拟合效果较好。


7. 希尔排序

(1)算法实现原理

①选择一个增量序列t 1 , t 2 , … … , t k,其中 t i > t j , t k = 1

②按增量序列个数k kk,对序列进行k kk趟排序;

③每趟排序,根据对应的增量t1 ,将待排序列分割成若干长度为m mm的子序列,分别对各子表进行直接插入排序。仅增量因子为1 11时,整个序列作为一个表来处理,表长度即为整个序列的长度。

下面以使用冒泡排序49,38,65,97,76,13,27,49,55,4这十个数字为例,以图示介绍希尔排序,见下图


16ec07bbcf5743f99ee0a119e2d28c07.png

(2)源代码

SHELLSORT(A)
input: an array a of length n with array elements numbered 0 to n − 1
inc ← round(n/2)
while inc > 0 do:    
    for i = inc .. n − 1 do:   
        temp ← a[i]        
        j ← i        
        while j ≥ inc and a[j − inc] > temp do: 
            a[j] ← a[j − inc]  
            j ← j − inc        
        a[j] ← temp    
    inc ← round(inc / 2)

(3)算法性能分析

①时间复杂度分析:

  与其他算法相比,希尔排序并没有实际确定的时间复杂度,其时间复杂度与步长序列的选取有很大关系。

  例如当选择不同步长序列时,有如下时间复杂度关联表:

b0ab74dee85c48cd862c900aa922a244.png

②数据测试分析

使用随机数生成器生成了从 101102

的不同数据量级的随机数,通过使用希尔排序进行排序并获取了程序运行时间。为降低偶然性,对20组数据取平均值,做表如下:

a2760786991e433da71fb81b9ede6e46.png


通过获得的数据,使用Tableau做图像如图:

0f9a705e3cb049d291abf0ca5955c43d.png

从上表和上图中可以发现整体图像成直线大致符合理论的时间复杂度。


8. 堆排序


(1)算法实现原理

①创建一个初始堆H

②把堆首(最大值)和堆尾互换;

③把堆的尺寸缩小 1,并调整堆。

④重复②,直至堆的大小为1


下面以使用堆排序4,5,3,0,1,7,2,6这八个数字为例,以图示介绍堆排序,见下图

3dafb47c08744f5ebac840f26191733d.png


30ab20747c1747bab7432f7f970dd5f2.png


以上即为堆排序中一次调整的过程,当调整完一次后,使堆的大小减一,并重新调整堆,直至最后堆的大小为1,即可完成排序。


(2)源代码


HEAP-SORT(A):
    BUILD-MAX-HEAP(A);     //构建最大堆
    for i = A.length downto 2:
        exchange A[i] and A[1];
        A.heap-size = A.heap-size-1;
        MAX-HEAPIFY(i);
构建大顶堆伪代码:
BUILD-MAX-HEAP(A):
    A.heap-size = A.length;
    //heap-size代表整个数组中在堆中的元素个数
    for i = A.length/2 downto 1:
        MAX-HEAPIFY(i)
维护大顶堆伪代码:
MAX-HEAPIFY(A, i):        //维护堆性质的关键, 用于检测是否满足堆的性质
    l = left(i);
    r = right(i);   //记录左右孩子的下标
    if l <=  A.heap-size and A[l] >= A[i]:
        largest = l;    //记录根节点和左右孩子中最大数的下标
    else :
        largest = r;
    if r <= A.heap-size and r >= A[largest]:
        largest = r;
    if i != largest:
        exchange A[i] and A[largest];
        MAX-HEAPIFY (A, largest);


(3)算法性能分析

①时间复杂度分析:

 堆排序的排序过程主要分为构建初始堆和进行排序两部分,接下来分别进行时间复杂度分析:

a.初始化建堆:

 初始化建堆只需要对二叉树的非叶子节点调用调整堆函数,由下至上,由右至左选取非叶子节点来进行调整。那么倒数第二层的最右边的非叶子节点就是最后一个非叶子结点。

 假设高度为k kk,则从倒数第二层右边的节点开始,这一层的节点都要执行子节点比较然后交换(如果顺序是对的则无需交换);倒数第三层则会选择其子节点进行比较和交换,如果没交换就可以不用再执行下去了。如果交换了,那么又要选择一支子树进行比较和交换;高层也是这样逐渐递归。

那么总的时间计算为:


31df28ad1cfe4ad6b40484a1d302ad9c.png

则堆排序初始堆的时间复杂度为:O ( n )


b. 排序重建堆

在取出堆顶点放到对应位置并把原堆的最后一个节点填充到堆顶点之后,需要对堆进行重建,只需对堆的顶点调用调整堆函数。

每次重建意味着有一个节点出堆,所以需要将堆的容量减一。调整堆函数的时间复杂度k = log,k 为堆的层数。所以在每次重建时,随着堆的容量的减小,层数会下降,函数时间复杂度会变化。重建堆一共需要n − 1次循环,每次循环的比较次数为log ⁡ i ,则有:


a79a756ce4cc47b1aa0592f07966062a.png

即log ⁡ n ! 与n log ⁡ n 是同阶函数:

 

 则堆排序的时间复杂度为O ( n log ⁡ n )


②数据测试分析:

使用随机数生成器生成了从

101106的不同数据量级的随机数,通过使用堆排序进行排序并获取了程序运行时间。为降低偶然性,对20组数据取平均值,选择105时运行时间为理论值并做表如下

d0a98e3454af45e4bdfbc18f58362b72.png

通过获得的数据,使用105时的时间消耗为基准理论值,使用Tableau做图像并拟合如图:


13cec60bb0f04ec19d84c3828599c364.png

结合以上图标图不难得出:整体时间消耗都大致满足O ( n log ⁡ n ) )的时间复杂度。但对于101,102两组数据存在较大偏差。通过对算法进行分析不难得出,这两组时堆排序的运行时间过短。主要运行时间来自于生成初始堆以及内存开辟而不是进行排序,因此,时间会略大于理论值。

 当数据较大时,主要时间消耗来自于排序的时间消耗,故拟合效果较好。

(二)多种算法性能比较

我们分别对每个算法的效率以及实现方法探究完毕后,不禁产生如下疑问:对于不同数量级的数据,应该如何选择算法,以更高效的完成排序呢?为探究如上问题,特进行如下实验:

对于以上八种排序算法,分别进行从10 1到10 6数量级的20次测试,分别以时间消耗值和时间消耗对数值做表如下:

c03ebf1b2c3042eeb128b3781d923504.png

为了分析各数值间对比关系,对数据进行可视化处理,以时间消耗对数值做折线图如下:


203e5186f5f94c0a801f4aa573ee3b2d.png


 可以看到,对于数量级很小时除基数排序与合并排序外的六种排序方式都比较省时间。当数量级界于102103之间时,八种排序算法的时间消耗差距不大。但当数据量较大时,基数排序,快速排序,合并排序和堆排序有明显的优势。而冒泡排序,选择排序和插入排序则需要消耗较多的时间完成排序操作。

 对于较少数据量级时,插入排序表现出了很优秀的性能,甚至比快速排序还要快,这是因为与快速排序相比:

 ①插入排序没有额外内存的申请和释放开销

 ②插入排序没有递归栈的开销


 对于时间复杂度同为O ( n 2 ) 的插入排序,选择排序和冒泡排序而言,对于整个101~106

的数量级均有插入排序时间最短,冒泡排序时间最长。这是由于冒泡排序中比较次数的时间复杂度为O ( n 2 ) O,且只要顺序相反,就需要对两个数的顺序进行调换,产生很多次中间的不必要调换操作,从而延长了运行所需时间。

 选择排序和插入排序相比较,插入排序更快。其主要原因可能为选择排序需要从序列中找到当前最大或最小的值才能进行排序,因此每次都需要与子序列中的全部元素进行比较。而插入排序无需比较子序列全部元素,只需要找到当前序列第一个比自己大或小的元素,将自身插入到其前一个位置即可。因此插入排序的时间略小于选择排序。


 对于其他五个时间复杂度为O ( n ∗ log ⁡ n ) 的排序算法,快速排序出现最差的情况并不是由于输入数据,而是选取到的随机数本身,选到极端的情况非常小,所以对于绝大部分数据而言都是能达O ( n ∗ log ⁡ n ),而合并排序需要较多赋值的语句,受输入数据的影响比较大,因此当数据规模较大时,不受输入数据影响的快速排序快于合并排序。对于堆排序,实现过程中需要反复调整堆的结果,赋值调整的次数也较多,因此时间消耗也相对较大。而基数排序只对各个数位进行排序,大大缩减了排序所需的时间,因而时间消耗最小。


 对于整体八种算法,不难看出,对于数据量较小时,时间复杂度为O ( n 2 )的算法与时间复杂度为O ( n ∗ log ⁡ n ) O的算法区别不大,都能较好的完成排序。而当数据量较大时,时间复杂度为O ( n ∗ log ⁡ n ) 的算法体现出明显优势,与时间复杂度为O ( n 2 ) 的算法相比节省了很多时间。因此当数据量较小时,可以选择除基数排序与合并排序外的六种排序方式进行排序。但当数据量较大时,则应该选择基数排序或快速排序进行排序操作。

相关文章
|
3月前
|
机器学习/深度学习 边缘计算 算法
NOMA和OFDMA优化算法分析
NOMA和OFDMA优化算法分析
239 127
|
5月前
|
数据采集 机器学习/深度学习 算法
别急着上算法,咱先把数据整明白:大数据分析的5个基本步骤,你都搞对了吗?
别急着上算法,咱先把数据整明白:大数据分析的5个基本步骤,你都搞对了吗?
229 4
|
2月前
|
存储 搜索推荐 算法
加密算法、排序算法、字符串处理及搜索算法详解
本文涵盖四大类核心技术知识。加密算法部分介绍了对称加密(如 AES)、非对称加密(如 RSA)、哈希摘要(如 SHA-2)、签名算法的特点及密码存储方案(加盐、BCrypt 等)。 排序算法部分分类讲解了比较排序(冒泡、选择、插入、归并、快排、堆排序)和非比较排序(计数、桶、基数排序)的时间复杂度、适用场景及实现思路,强调混合排序的工业应用。 字符串处理部分包括字符串反转的双指针法,及项目中用正则进行表单校验、网页爬取、日志处理的实例。 搜索算法部分详解了二分查找的实现(双指针与中间索引计算)和回溯算法的概念(递归 + 剪枝),以 N 皇后问题为例说明回溯应用。内容全面覆盖算法原理与实践
128 0
|
2月前
|
人工智能 自然语言处理 算法
2025 年 7 月境内深度合成服务算法备案情况分析报告
2025年7月,中央网信办发布第十二批深度合成算法备案信息,全国389款产品通过备案,服务提供者占比超七成。截至7月14日,全国累计备案达3834款,覆盖文本、图像、音视频等多模态场景,广泛应用于生活服务、医疗、金融等领域。广东以135款居首,数字人、AI客服等C端应用主导,民营企业成主力,国企聚焦公共服务。随着AI政策推动,备案已成为AI产品合规上线关键环节。
|
5月前
|
存储 监控 算法
员工行为监控软件中的 Go 语言哈希表算法:理论、实现与分析
当代企业管理体系中,员工行为监控软件已逐步成为维护企业信息安全、提升工作效能的关键工具。这类软件能够实时记录员工操作行为,为企业管理者提供数据驱动的决策依据。其核心支撑技术在于数据结构与算法的精妙运用。本文聚焦于 Go 语言中的哈希表算法,深入探究其在员工行为监控软件中的应用逻辑与实现机制。
146 14
|
6月前
|
自然语言处理 算法 安全
境内深度合成服务算法备案通过名单分析报告
本报告基于《境内深度合成服务算法备案通过名单》,分析了2023年6月至2025年3月公布的10批备案数据,涵盖属地分布、行业应用及产品形式等多个维度。报告显示,深度合成算法主要集中于经济发达地区,如北京、广东、上海等地,涉及教育、医疗、金融、娱乐等多行业。未来趋势显示技术将向多模态融合、行业定制化和安全合规方向发展。建议企业加强技术研发、拓展应用场景、关注政策动态,以在深度合成领域抢占先机。此分析旨在为企业提供参考,助力把握技术发展机遇。
境内深度合成服务算法备案通过名单分析报告
|
6月前
|
供应链 算法 搜索推荐
从公布的前十一批其他算法备案通过名单分析
2025年3月12日,国家网信办发布算法备案信息,深度合成算法通过395款,其他算法45款。前10次备案中,深度合成算法累计3234款,其他类别647款。个性化推送类占比49%,涵盖电商、资讯、视频推荐;检索过滤类占31.53%,用于搜索优化和内容安全;调度决策类占9.12%,集中在物流配送等;排序精选类占8.81%,生成合成类占1.55%。应用领域包括电商、社交媒体、物流、金融、医疗等,互联网科技企业主导,技术向垂直行业渗透,内容安全和多模态技术成新增长点。未来大模型检索和多模态生成或成重点。
从公布的前十一批其他算法备案通过名单分析
|
7月前
|
存储 搜索推荐 算法
算法系列之排序算法-堆排序
堆排序(Heap Sort)是一种基于堆数据结构的比较排序算法。它的时间复杂度为 $O(nlogn)$,并且是一种原地排序算法(即不需要额外的存储空间)。堆排序的核心思想是利用堆的性质来维护一个最大堆或最小堆,然后逐步将堆顶元素(最大值或最小值)取出,放到数组的末尾,最终得到一个有序的数组。
183 8
算法系列之排序算法-堆排序
|
6月前
|
人工智能 自然语言处理 供应链
从第十批算法备案通过名单中分析算法的属地占比、行业及应用情况
2025年3月12日,国家网信办公布第十批深度合成算法通过名单,共395款。主要分布在广东、北京、上海、浙江等地,占比超80%,涵盖智能对话、图像生成、文本生成等多行业。典型应用包括医疗、教育、金融等领域,如觅健医疗内容生成算法、匠邦AI智能生成合成算法等。服务角色以面向用户为主,技术趋势为多模态融合与垂直领域专业化。
|
6月前
|
JavaScript 前端开发 算法
JavaScript 中通过Array.sort() 实现多字段排序、排序稳定性、随机排序洗牌算法、优化排序性能,JS中排序算法的使用详解(附实际应用代码)
Array.sort() 是一个功能强大的方法,通过自定义的比较函数,可以处理各种复杂的排序逻辑。无论是简单的数字排序,还是多字段、嵌套对象、分组排序等高级应用,Array.sort() 都能胜任。同时,通过性能优化技巧(如映射排序)和结合其他数组方法(如 reduce),Array.sort() 可以用来实现高效的数据处理逻辑。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~

热门文章

最新文章