8、排序(sort)
拓扑排序,与其他排序算法一览
最后更新于
拓扑排序,与其他排序算法一览
最后更新于
例如,假定一个计算机专业的学生必须完成图3-4所列出的全部课程。从图中可以清楚地看出各课程之间的先修和后续的关系。如课程C5的先修课为C2,后续课程为C4和C6。通常,我们把这种顶点表示活动、边表示活动间先后关系的有向图称做顶点活动网(Activity On Vertex network),简称AOV网。
若图中从V到W有一条有向路径,则V一定排在W之前。满足该条件的顶点序列称为一个拓扑序
获得一个拓扑序的过程就是拓扑排序
AOV若有合理的拓扑序,则必定是有向无环图(Directed Acyclic Graph,DAG)
拓扑排序的思路是每次都找一个入度为0的顶点并输出,并且将该顶点所有邻接点入度减1。 可以看出,找入度为0的顶点是关键,若每次都要遍历那必定会耗费大量时间空间,所以更聪明的算法是,随时将入度变为0的顶点放入一个容器中。 伪码描述如下
模板代码:
08-图8 How Long Does It Take (25分) 是一道拓扑排序的变形,程序不算复杂,建议尝试; 题意、代码及思路指路博客:
AOE网络(Activity On Edge)网络
一般用于安排项目的工序
先推出最早完成时间 —— mint [ j ] = max( mint[ j ], mint[ i ]+edge[ i ][ j ])
再由后往前推出最晚完工时间—— maxt[ i ] = min( maxt[ j ], maxt[ j ]-edge[ i ][ j ])
即可得机动时间—— D[ i ][ j ] = maxt[ j ] - mint[ i ] - edge[ i ][ j ]
08-图8 How Long Does It Take (25分) 是一道拓扑排序的变形,求最早完成时间 08-图9 关键活动 (30分) 求关键路径 代码及思路指路博客:PTA数据结构题目集 第八周——图(下)
为简单起见,讨论整数的从小到大排序
N为正整数
只讨论基于比较的排序(> = < 有定义)
只讨论内部排序
稳定性:任意两个相等的数据排序前后的相对位置不发生改变
没有哪一种排序是任何情况下都表现最好的!
测试题目:09-排序1 排序 (25分)
它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小、首字母从Z到A)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完成。 这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。
——摘自百度百科
时间复杂度
最好情况:顺序,时间复杂度 T = O(N) 最坏情况:整个逆序,时间复杂度 T = O(N^2^)
优缺点
优点:简单易写,只需交换相邻元素,即使是单向链表也可直接排序,稳定(交换前后相等元素的位置不变) 缺点:时间复杂度较大,慢!
测试结果
插入排序,一般也被称为直接插入排序。对于少量元素的排序,它是一个有效的算法。插入排序是一种最简单的排序方法,它的基本思想是将一个记录插入到已经排好序的有序表中,从而一个新的、记录数增1的有序表。在其实现过程使用双层循环,外层循环对除了第一个元素之外的所有元素,内层循环对当前元素前面有序表进行待插入位置查找,并进行移动。
——摘自百度百科
时间复杂度
最好情况:顺序,时间复杂度 T = O(N) 最坏情况:整个逆序,时间复杂度 T = O(N^2^) 一般情况下时间复杂度下界计算: 交换两个相邻元素正好消去1个逆序对 设有I个逆序对 则T(N,I) = O(N+I)
优缺点
优点:稳定 缺点:比较次数不一定,比较次数越少,插入点后的数据移动越多,尤其是当数据总量庞大的时候
测试结果
如何提高效率
有定理如下
任意N个不同元素组成的序列平均具有N(N-1)/4个逆序对
任何仅以交换相邻两元素来排序的算法,其平均时间复杂度为O(N^2^)
所以要提高算法效率,我们必须
每次消去不止1个逆序对!
每次交换相隔较远的2个元素!
利用了插入排序的简单,克服插入排序只能交换相邻两元素的缺点。
希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至 1 时,整个文件恰被分成一组,算法便终止。
——摘自百度百科
定义增量序列DM >DM-1>…>D1 = 1 对每个Dk进行“Dk间隔”排序(k=M,M-1,……,1) 注意:“Dk间隔”有序的序列,在执行“Dk-1间隔”排序后,仍然是“Dk间隔”有序的!
希尔增量序列选取
原始希尔排序增量序列 DM = N/2, Dk = Dk+1 / 2
Hibbard增量序列
Dk = 2^k^-1 ——相邻元素互质
Sedgewick增量序列等
优缺点
优点:快,数据移动少!适用于数据量较大的情况 缺点:不同的增量序列选取会导致算法复杂度差异,如何选取增量序列只能根据经验,不稳定
测试结果
在介绍堆排序前,先介绍选择排序,老朋友了
时间复杂度
无论如何复杂度都为O(N^2^)
测试结果
这里以排成升序为例,我们需要将其原始数组调整成下标从0开始的最大堆,再将最大堆顶与当前最后的元素交换(相当于删除最大堆顶)后调整
时间复杂度
堆排序给出了最佳的平均时间复杂度 最好情况O(nlogn) 最坏情况O(nlogn) 平均时间复杂度O(nlogn)
优缺点
优点: 快!即使是最坏情况下性能也很优越,使用的辅助空间少 缺点:不稳定,不适合对象的排序。
测试结果
核心是有序子列的合并,这里给出递归实现的版本~非递归实现看这里哦归并排序循环实现
时间复杂度
最好情况O(nlogn) 最坏情况O(nlogn) 平均时间复杂度O(nlogn)
优缺点
优点: 稳定、快 缺点:较占用空间
测试结果
1.设k = a[0],将k挪到适当位置,使得比k小的元素都在k左边,比k大的元素都在k右边(在O(n)时间完成) 2.把k左边的部分快速排序 3.把k右边的部分快速排序 k为主元
时间复杂度
最好情况每次正好中分O(nlogn) 最坏情况O(N^2^) 平均时间复杂度O(nlogn)
优缺点
优点: 是所有内部排序的最快的算法 缺点:不稳定,最坏情况下效率较慢!
测试结果
时间复杂度
最好情况初始即有序 最坏情况有N/2个环,每个环包含2个元素,交换两个元素需要走三步,需要3N/2次元素移动 T = O(m N),m是每个A元素的复制时间
基数排序的代码我参考了这篇博客的,用的方法非常巧妙:基数排序
时间复杂度
N为待排序元素个数,而B是桶数 O(P(N+B)) 一趟分配时间为O(N),一趟收集时间复杂度为O(B),共进行P趟分配和收集
优缺点
优点:适用于位数不多,待排序列最大位数不是特别大的情况,快 缺点:空间换时间
测试结果
在AOE网络中,活动是表示在边上的,顶点被分为三个部分:顶点编号、最早完成时间和最晚完成时间
而关键路径,是由绝对不允许延误的活动组成的路径,即没有机动时间的路径。
测试结果如下,有3个样例没过
测试结果如下,挺给力的
增量元素不互质,则小增量可能根本不起作用!
可以看到耗时都没超过100ms,在这些测试样例里的速度还是很理想的
测试结果如下,虽然都能过,但后几个样例耗时都很大
测试结果如下,好像比希尔排序给力些哦~
测试结果如下,你品,你细品
测试结果如下
当数据量大且待排序的元素为对象、移动所需时间特别高时,我们需要间接排序 定义一个指针数组作为“表”(table),记录待排元素
之前讲的算法都需要比较,最坏情况下也都有Nlogn,还能更快吗? 假设我们有N个学生,他们的成绩是0~100之间的整数(于是有M = 101个不同的成绩值),如何在线性时间内将学生按成绩排序 LSD主位优先 MSD次位优先
测试结果如下,超快的说~