常见的排序算法——归并排序(四)

news/2024/11/16 12:03:31/文章来源:https://www.cnblogs.com/green-cnblogs/p/18201378

本文记述了针对归并排序的 3 项改进和一份参考实现代码,并在说明了算法的性能后用随机数据进行了验证。

◆ 思想

本文实现了《算法(第4版)》书中提到的 2 项改进和练习题 2.2.10。

  • 对小规模子数组使用插入排序。因为递归会使小规模问题中方法的调用过于频繁,所以改进对它们的处理方法就能改进整个算法。
  • 测试数组是否已经有序。任意有序的子数组算法的运行时间就变成线性的了。
  • 快速归并。在 merge() 函数中,按降序将待排序数组的后半部分复制到辅助数组中,然后将其归并到待排序数组中。这样就可以去掉内循环中检测某半边是否用尽的代码。

◆ 实现

排序代码采用《算法(第4版)》的“排序算法类模板”实现。(代码中涉及的基础类,如 Array,请参考算法文章中涉及的若干基础类的主要API)

// merge4.hxx...class Merge4
{...template<class _T,class = typename std::enable_if<std::is_base_of<Comparable<_T>, _T>::value>::type>staticvoidsort(Array<_T> & a){Array<_T> aux = Array<_T>(a.size());__sort__(a, 0, a.size()-1, aux);}...template<class _T,class = typename std::enable_if<std::is_base_of<Comparable<_T>, _T>::value>::type>staticvoid__sort__(Array<_T> & a, int lo, int hi, Array<_T> & aux){if (hi - lo <= 15) {                            // #1for (int i = lo+1; i <= hi; ++i)for (int j = i; j > lo && __less__(a[j], a[j-1]); --j)__exch__(a, j, j-1);} else {int mi = lo + (hi - lo) / 2;__sort__(a, lo, mi, aux);__sort__(a, mi+1, hi, aux);if (__less__(a[mi], a[mi+1]))               // #2for (int i = lo; i <= hi; ++i)aux[i] = a[i];else__merge__(a, lo, mi, hi, aux);}}...template<class _T,class = typename std::enable_if<std::is_base_of<Comparable<_T>, _T>::value>::type>staticvoid__merge__(Array<_T> & a, int lo, int mi, int hi, Array<_T> & aux){int i = lo, j = hi;for (int k = lo; k <= mi; ++k)aux[k] = a[k];for (int k = mi+1; k <= hi; ++k)               // #3aux[k] = a[hi - (k - (mi+1))];for (int k = lo; i <= j; ++k)                     // #4if (__less__(aux[i], aux[j])) a[k] = aux[i++];else                          a[k] = aux[j--];}...template<class _T,class = typename std::enable_if<std::is_base_of<Comparable<_T>, _T>::value>::type>staticbool__less__(_T const& v, _T const& w){return v.compare_to(w) < 0;         // #5}...

当待排序的子数组规模较小时,采用插入排序(#1)。测试数组是否已经有序,可确保有序的子数组算法的运行时间变成线性的(#2)。按降序将待排序数组的后半部分复制到辅助数组中(#3),然后将其归并到待排序数组中(#4)。将 '<' 改为 '>',即得到逆序的结果(#5)。

◆ 性能

对 __merge()__ 函数的改进,导致了不稳定的归并。

时间复杂度 空间复杂度 是否稳定
N*log(N) N

◆ 验证

测试代码采用《算法(第4版)》的倍率实验方案,用随机数据验证其正确性并获取时间复杂度数据。

// test.cpp...time_trial(int N)
{Array<Double> a(N);for (int i = 0; i < N; ++i) a[i] = Std_Random::random();    // #1Stopwatch timer;Merge4::sort(a);                     // #2double time = timer.elapsed_time();assert(Merge4::is_sorted(a));            // #3return time;
}...test(char * argv[])
{int T = std::stoi(argv[1]);          // #4double prev = time_trial(512);Std_Out::printf("%10s%10s%7s\n", "N", "Time", "Ratio");for (int i = 0, N = 1024; i < T; ++i, N += N) {            // #5double time = time_trial(N);Std_Out::printf("%10d%10.3f%7.2f\n", N, time, time/prev);   // #6prev = time;}
}...

用 [0,1) 之间的实数初始化待排序数组(#1),打开计时器后执行排序(#2),确保得到正确的排序结果(#3)。整个测试过程要执行 T 次排序(#4)。每次执行排序的数据规模都会翻倍(#5),并以上一次排序的时间为基础计算倍率(#6),

此测试在实验环境一中完成,

$ g++ -std=c++11 test.cpp std_out.cpp std_random.cpp stopwatch.cpp type_wrappers.cpp$ ./a.out 15N      Time  Ratio1024     0.008   2.672048     0.018   2.254096     0.040   2.228192     0.087   2.1716384     0.187   2.1532768     0.401   2.1465536     0.855   2.13131072     1.815   2.12262144     3.837   2.11524288     8.097   2.111048576    17.023   2.102097152    35.708   2.104194304    73.782   2.078388608   153.411   2.0816777216   319.890   2.09

可以看出,随着数据规模的成倍增长,排序所花费的时间将是上一次规模的 2.1? 倍,且在不断变小。将数据反映到以 2 为底数的对数坐标系中,可以得到如下图像,

test_data

O(N*log(N)) 代表了线性对数级别复杂度下的理论排序时间,该行中的数据是以 Time 行的第一个数据为基数逐一乘 2 + 2/log(N) 后得到的结果(因为做的是倍率实验,所以乘 (2*N*log(2*N)) / (N*log(N)),化简得到 2 + 2/log(N),即乘 2+2/log(1024),2+2/log(2048),2+2/log(4096),... 2+2/log(16777216);因为是二分归并,所以 log 的底数为 2)。

◆ 最后

完整的代码请参考 [gitee] cnblogs/18201378 。

写作过程中,笔者参考了《算法(第4版)》的归并排序、练习题 2.2.10、“排序算法类模板”和倍率实验。致作者 Sedgwick,Wayne 及译者谢路云。

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

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

相关文章

食物识别系统Python+深度学习人工智能+TensorFlow+卷积神经网络算法模型

一、介绍 食物识别系统。该项目通过构建包含11种常见食物类别(包括Bread, Dairy product, Dessert, Egg, Fried food, Meat, Noodles-Pasta, Rice, Seafood, Soup, Vegetable-Fruit)的图片数据集,并利用TensorFlow框架下的ResNet50神经网络模型进行开发。项目流程包括数据预…

和谷歌Google I/O杠上了,ChatGPT将具备通话功能

在当今社会,人工智能技术的发展已经取得了巨大的成就,尤其是在语言领域。ChatGPT作为一种新型的自然语言处理模型,被广泛应用于各种领域,比如问答系统、智能对话系统等,其在对话生成方面的表现也十分出色。而随着技术的不断发展,有人开始猜测ChatGPT是否将来具备通话功能…

CSP历年复赛题-P1014 [NOIP1999 普及组] Cantor 表

原题链接:https://www.luogu.com.cn/problem/P1014 题意解读:根据z字形遍历,求第n个数。 解题思路: 根据题意,遍历顺序如下图所示观察得知,第i层的x/y的x+y = i + 1,并且 如果i是偶数,x从1开始枚举;如果i是奇数,x从i开始枚举 100分代码: #include <bits/stdc++.h…

项目管理之八大绩效域------笔记(五)

18.7 度量绩效域度量绩效域涉及评估项目绩效和采取应对措施相关的活动和职能度量是评估项目绩效,并采取适当的应对措施,以保持最佳项目绩效的过程。一、 预期目标:①对项目状况充分理解;(随时对项目有充分了解) ②数据充分,可支持决策; ③及时采取行动,确保项目最佳绩效;…

CGCL论文阅读笔记

Candidate–aware Graph Contrastive Learning for Recommendation论文阅读笔记 Abstract 现存问题: ​ 大多数基于gcl的方法使用启发式数据增强方法,即随机节点/边下降和属性掩蔽,来构造对比对,导致重要信息的丢失。 解决方案: ​ 为了解决基于gcl的方法中的问题,我们提…

如何使用抽象,虚方法,重写,看了这个示例,超级好用

基础参数 interface,abstract,virtual,override 然后实现了这样的效果 同一个方法,调用不同的实现类,那实现不一样的结果 具体代码public interface IStatsBase{string UserSpeak(string content);string UserHandel(string content);}public interface IUserStatsBase{s…

如何使用虚方法,重写,接口那实现不一样的结果

基础参数 interface,abstract,virtual,override 然后实现了这样的效果 同一个方法,调用不同的实现类,那实现不一样的结果 具体代码public interface IStatsBase{string UserSpeak(string content);string UserHandel(string content);}public interface IUserStatsBase{s…

Calico 组网(Networking)

确定最佳网络选项 了解 Calico 支持的不同网络选项,以便您可以选择最适合您需求的选项。Calico 灵活的模块化架构支持广泛的部署选项,因此您可以选择适合您的特定环境和需求的最佳网络方法。这包括能够以非覆盖或覆盖模式、带或不带 BGP 运行各种 CNI 和 IPAM 插件以及底层网…

Unity中Reorderable List用法(待维护)

Unity官方文档里完全没有提到ReorderableList类,这是因为它不在UnityEngine或UnityEditor的命名空间下,而是在UnityEditorInternal命名空间下,这个命名空间里的东西是没有官方文档支持的ReorderableList的作用 它的作用,是让一个数组,在Unity的Inspector界面上显示得更好一…

【质量管理】核对单和核查表的区别

总结:核对单和核查表的区别郭慕荣博客园

Windows包管理工具chocolatey安装

Windows软件管理工具chocolatey安装 背景:chocolatey作为windows软件管理工具下载JDK等工具,可以避免下载工具,修改环境变量配置,操作方便 安装步骤 官方指导个人操作以管理员身份运行Poweshell按照官网指导调整执行策略PS C:\WINDOWS\system32> Get-ExecutionPolicy Re…

MySQL全文索引源码剖析之Insert语句执行过程

全文索引是信息检索领域的一种常用的技术手段,用于全文搜索问题本文分享自华为云社区《MySQL全文索引源码剖析之Insert语句执行过程》 ,作者:GaussDB 数据库。1. 背景介绍 全文索引是信息检索领域的一种常用的技术手段,用于全文搜索问题,即根据单词,搜索包含该单词的文档…