| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 数据结构与算法 -> [Alg]排序算法之选择排序 -> 正文阅读 |
|
[数据结构与算法][Alg]排序算法之选择排序 |
[Alg]排序算法之选择排序作者:屎壳郎 miaosg01@163.com 时间:Aug 2021 版次:初版 简介: 当数据量较大时,堆排序优于shell排序。但无论如何比不过快速排序。快排的效率只是平均意义上的(概率上说),最坏的情况会退化至 N 2 N^2 N2。堆排序的优势在于,在恶化的情况下也能保证时间复杂度在 O ( N lg ? N ) O(N\lg N) O(NlgN)。后续我们会讲到归并排序也有这个特性,任何情况下都能保证 N lg ? N N\lg N NlgN,但归并排序要求更多的空间。 1、直接选择排序直接选择排序遍历所有记录N,确定最大值(或最小值),然后输出到最终位置。缩小范围至剩余N-1,重复这个步骤。这个过程和直接插入排序完全相反,直接插入排序是从1个记录开始,一个接一个的往里插入记录,并逐步扩大遍历范围。选择排序直接确定记录的最终位置,而插入排序直到插入最后一个记录后在能确定记录的最终位置。 算法S:(直接选择排序)设记录 R 1 , R 2 , … , R N R_1,R_2,\ldots,R_N R1?,R2?,…,RN?,其键值 K 1 , K 1 , … , K N K_1,K_1,\ldots,K_N K1?,K1?,…,KN?。
要从N个记录中确定最大值,至少比较N-1次,所以直接选择排序的时间复杂度 O ( N 2 ) O(N^2) O(N2)。比较次数是没办法节省的,难道就没有改进的余地了吗?要改进算法的效率,无外乎两种途径,其一是对要待解决问题的本质有深入的洞察,并有深厚的数学功底来确立模型,建立高效算法。这点好像比较难。另一个相对简单的方法,不要每次都从零开始,要善于并充分利用前序的工作成果来减小后续的工作量或改善后续工作的复杂度。 对于第一种改进算法的方式,我们直接跳过就好了,毕竟那是计算机专家兼数学家或数学家兼计算机专家的饭碗,做人不能做绝了,给他们留口饭吃。我们重点来讲后一种方法。 我们在遍历 R j … R 1 R_j\ldots R_1 Rj?…R1?过程中,如果没有第一次就选中最大值,遍历过程会发生替换,产生一个序列。例如(1 3 2 5 4)第一次选 m a x = 4 max=4 max=4,继续向左搜索,找到 5 > 4 5 > 4 5>4,替换 m a x = 5 max=5 max=5,确定5为最大值。直接选择排序没有充分利用这个序列,直接舍弃4(第二大值),下次又从头开始。虽然第一次遍历不能减少比较次数,但我们可以充分利用遍历过程产生的有价值信息。 直接选择排序改进1对算法做如下改进: 2、方根选择另一个利用前序比较结果的例子是分组,这种方法不需要额外的空间存储位置信息。下面介绍称之为方根选择的分组选择方法。设
N
N
N 项记录,分为
N
\sqrt N
N? 组,每组
N
\sqrt N
N? 项。 我们选出最大数908后,要确定第二大值,只需在(170,897,275)中确定最大值,然后与其它组的最大值比较(512,653,765)。我们从上图看到,不影响不含最大值的项。故方根选择要在选出最大值的组内进行 ( N ? 2 ) (\sqrt N -2) (N??2)次比较,与其它组进行 ( N ? 1 ) (\sqrt N -1) (N??1)次比较,其复杂度 O ( N N ) O(N\sqrt N) O(NN?)。 推广开来,我们也扩展至立方根,即 N N N 分 N 3 \sqrt[3] N 3N?组,每组包含 N 3 \sqrt[3] N 3N?小组,每小组 N 3 \sqrt[3] N 3N?个项,可降至 O ( N N 3 ) O(N\sqrt[3]N) O(N3N?)。 如果我们再推广,再分,再分,再分…最后每个组只含一个项的时候,是棵树。记得我们在《[Alg] 排序算法之插入排序》中提到两个算法改进,直接插入算法为减少搜索长度改为两路插入,再改进四路,再分,再分,再分…是棵树;链表插入中,为减小链表长度,再分,再分,再分…是棵树!!!我们好像发现了一个秘密!!!好多数据结进化的终点都是棵树!!! 3、选择树我们先考虑一个典型的树选择的例子——乒乓球淘汰赛:
4、堆排序堆的定义:定义 K 1 , K 2 , … , K N K_1,K_2,\ldots,K_N K1?,K2?,…,KN?满足下列条件称之为堆。 K ? j / 2 ? ≥ K j f o r ? 1 ≤ ? j / 2 ? < j ≤ N K_{\lfloor j/2\rfloor}\geq K_j \qquad for\, 1\leq\lfloor j/2\rfloor<j\leq N K?j/2??≥Kj?for1≤?j/2?<j≤N 根据堆的定义,知
K
1
≥
K
2
K_1\geq K_2
K1?≥K2?,
K
1
≥
K
3
K_1\geq K_3
K1?≥K3?,
K
2
≥
K
4
K_2\geq K_4
K2?≥K4?。 根据我们优良的革命传统,举个例子说明:(5 9 1 8 2 6 4 7 3)。设LOC(k)代表位置k。 第一步: l = 4 l=4 l=4 LOC(4)8>LOC(8)7和LOC(9)3。满足堆条件。 l = l ? 1 l=l-1 l=l?1。 第二步: l = 3 l=3 l=3 LOC(3)1<LOC(6)6和LOC(7)4,LOC(3)1与大者LOC(6)6交换。 l = l ? 1 l=l-1 l=l?1。 第三步: l = 2 l=2 l=2 无需交换。 第四步:
l
=
1
l=1
l=1 取
R
=
5
R=5
R=5,与子结点LOC(2)LOC(3)比较,LOC(2)9大,上浮;继续与LOC(2)的子结点LOC(4)LOC(5)比较,LOC(4)8大,上浮至LOC(2),继续与LOC(4)的子结点LOC(8)LOC(9)比较,LOC(8)大,上浮至LOC(4),无子结点
R
=
5
R=5
R=5放入LOC(8)。第四步详细过程见图示: 见表(前四步为建堆阶段,后面为选择排序阶段)。
算法H:(堆排序)
了解一个算法最有效的方法是拿出纸和笔实际跑一遍,掌握一个算法最有效的方法是编写程序运行一遍。别无它法! 从上面的表格看起来很难让人相信堆排序是一种高效的算法。当 N N N较小是确实如此,但随着 N N N增大,堆排序优于shell排序。但无论如何比不过快速排序。然而堆排序相对于快排有另一方面的优势,快排的效率只是平均意义上的(概率上说),最坏的情况会退化至 N 2 N^2 N2。堆排序的优势在于,在恶化的情况下也能保证时间复杂度在 O ( N lg ? N ) O(N\lg N) O(NlgN)。后续我们会讲到归并排序也有这个特性,任何情况下都能保证 N lg ? N N\lg N NlgN,但归并排序要求更多的存储空间。 堆排序边界条件问题编程实践中一个很大的麻烦就是处理边界条件,很多错误往往也出现在边界条件上,我们看到堆排序算法H4中,有三个条件分支,分别为 j < r j<r j<r, j = r j=r j=r和 j > r j>r j>r,都是为了防止插入操作越界。可以采用《[Alg]排序算法之插入排序》中提到的第一种应对措施,即在 N + 1 N+1 N+1处插入 K N + 1 = ? ∞ K_{N+1}=-\infty KN+1?=?∞确保 K ≥ K N + 1 K\geq K_{N+1} K≥KN+1?。为了在程序运行都满足这个条件,我们还要对其它方面进行改造,H2改为 R r + 1 ← R N + 1 R_{r+1}\gets R_{N+1} Rr+1?←RN+1?,同样,在 r = 1 r=1 r=1之后置 R 2 ← R N + 1 R_2\gets R_{N+1} R2?←RN+1?。这样的设置不同于在插入排序中作用,既不能使代码更简洁,也不能提高代码运行速度,只是减少了处理不当出错的机会。 堆排序的改进如果你认认真真的拿着纸和笔推演了一遍堆排序算法,你会发现一个问题。第一阶段建堆完成,进入选择阶段,从头部取最大值放在末尾,末尾的值重新插入堆中,这个过程特别长。因为我们每次从堆末尾取值,值都很小,从头部最大值向左侧小值搜索位置且几乎总是 K < K i K<K_i K<Ki?,几乎总是在接近堆末端插入,这要进行大量的对比操作(H6步中)。如何规避这种情况?我们可以采取另一个策略,不进行对比,整体移动数据,直到堆的末尾,然后从尾端向前搜索然后插入。 具体实现如下:
堆插入操作算法I:(堆插入)
删除堆内数据算法D:(删除堆内数据)给定位置 s s s,删除位置 s s s的数据。
|
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 | -2024/12/28 1:48:02- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |
数据统计 |