一、排序算法概述
常见的排序算法包括:
- 直接插入排序
- 冒泡排序
- 简单选择排序
- 希尔排序
- 快速排序
- 堆排序
- 归并排序
二、直接插入排序
1. 算法思想
基本做法:在插入第i个记录时,R₁,R₂,...,Rᵢ₋₁已经排好序,将记录Rᵢ的关键字kᵢ依次与关键字kᵢ₋₁,kᵢ₋₂,...,k₁进行比较,从而找到Rᵢ应该插入的位置,插入位置及其后的记录依次向后移动。
2. 排序过程示例
待排序列:35 12 67 29 51
第1步:[35] 12 67 29 51
第2步:[12 35] 67 29 51
第3步:[12 35 67] 29 51
第4步:[12 29 35 67] 51
第5步:[12 29 35 51 67] ✓完成
3. 一句话总结
按顺序插入待排关键字,插入时依次查找位置,直接插入,后面的依次后移。
三、冒泡排序
1. 算法思想
首先将第一个记录的关键字和第二个记录的关键字进行比较,若为逆序,则交换两个记录的值,然后比较第二个记录和第三个记录的关键字,依此类推。
2. 排序过程示例
待排序列:35 12 67 29 51
第一次冒泡排序:
① 12 35 67 29 51 (35和12交换)
② 12 35 67 29 51 (不交换)
③ 12 35 29 67 51 (67和29交换)
④ 12 35 29 51 67 (67和51交换)
第二次冒泡排序:
① 12 35 29 51 67 (不交换)
② 12 29 35 51 67 (35和29交换)
3. 一句话总结
依次把相邻的两个记录进行比较,然后交换位置。
四、简单选择排序
1. 算法思想
n个记录进行简单选择排序的基本方法是:通过n-i次关键字之间的比较,从n-i+1个记录中选出关键字最小的记录,并和第i(1 ≤ i ≤ n)个记录进行交换,当i等于n时所有记录有序排列。
2. 排序过程示例
待排序列:35 12 67 29 51
①:找出最小值12,与第一个关键字交换
[12 35 67 29 51]
②:找出剩下4个记录中的最小值29,与第二个关键字交换
[12 29 67 35 51]
③:找出剩下3个记录中的最小值35,与第三个关键字交换
[12 29 35 67 51]
④:找出剩下2个记录中的最小值51,与第四个关键字交换
[12 29 35 51 67]
3. 一句话总结
每次选择最小的,与第一个没有排过序的记录交换。
五、希尔排序
1. 算法思想
希尔排序又称"缩小增量排序",是对直接插入排序方法的改进。
先将整个待排记录序列分割成若干子序列,然后分别进行直接插入排序,待整个序列中的记录基本有序时,再对全体记录进行一次直接插入排序。
2. 排序过程示例
待排序列:48 37 64 96 75 12 26 48 54 03
d₁=5(距离为5的倍数的记录为同一组)
48 37 64 96 75 12 26 48 54 03
↑ ↑ ↑ ↑ ↑
第1组 第2组 第3组 第4组 第5组
排序后:[12 26 48 54 03] [48 37 64 96 75]
12 26 48 54 03 48 37 64 96 75
d₂=3(距离为3的倍数的记录为同一组)
12 26 48 54 03 48 37 64 96 75
↑ ↑ ↑ ↑ ↑
第1组 第2组 第3组 第4组 第5组
排序后:[12 03 48 37 26] [48 54 64 96 75]
12 03 48 37 26 48 54 64 96 75
d₃=1(距离为1的倍数的记录为同一组,即所有记录为一组)
12 03 48 37 26 48 54 64 96 75
排序后:03 12 26 37 48 48 54 64 75 96
3. 一句话总结
间隔若干个空的记录分为一组,进行直接插入排序,依次将间隔缩小到1为止。
六、快速排序
1. 算法思想
通过一趟排序将待排的记录划分为独立的两部分,称为前半区和后半区,其中前半区中记录的关键字均不大于后半区记录的关键字,然后再分别对这两部分记录继续进行快速排序,从而使整个序列有序。
2. 具体做法
附设两个位置指示变量i和j,它们的初值分别指向序列的第一个记录和最后一个记录。设枢轴记录(通常是第一个记录)的关键字为pivot,则:
- 首先从j所指位置起向前搜索,找到第一个关键字小于pivot的记录时将该记录向前移到i指示的位置
- 然后从i所指位置起向后搜索,找到第一个关键字大于pivot的记录时将该记录向后移到j所指位置
- 重复该过程直至i与j相等为止
3. 排序过程示例
待排序列:a[8] = {39, 58, 32, 47, 46, 19, 25, 55}
初始状态:
39 58 32 47 46 19 25 55
i j
i=1, j=8, pivot=39
第1次(从j向前搜索):
25 58 32 47 46 19 39 55
i j
i=1, j=7
第2次(从i向后搜索):
25 39 32 47 46 19 58 55
i j
i=2, j=7
第3次(从j向前搜索):
25 19 32 47 46 39 58 55
i j
i=2, j=6
第4次(从i向后搜索):
25 19 32 39 46 47 58 55
i j
i=4, j=6
第5次(从j向前搜索):
25 19 32 39 46 47 58 55
i j
i=j=4
一趟快速排序完成,以39为分界线,
前面的都比它小,后面的都比它大。
4. 一句话总结
设两个指针指示头尾,从尾开始,首尾交替轮流和枢轴记录(第一个记录)进行比较,并交换位置。
七、堆排序
1. 堆的定义
对于n个元素的关键字序列{k₁, k₂, ..., kₙ},当且仅当满足下列关系时称其为堆:
小顶堆:
kᵢ ≤ k₂ᵢ
kᵢ ≤ k₂ᵢ₊₁
大顶堆:
kᵢ ≥ k₂ᵢ
kᵢ ≥ k₂ᵢ₊₁
2. 堆的示例
96
/ \
75 64
/ \ / \
54 48 37 33
/ \
12 03
大顶堆:每个节点都大于等于其子节点
3. 算法思想
对一组待排序记录的关键字,首先把它们按堆的定义排成一个序列(即建立初始堆),从而输出堆顶的最小关键字(对于小顶堆而言)。然后将剩余的关键字再调整成新堆,便得到次小的关键字,如此反复,直到全部关键字排成有序序列为止。
4. 一句话总结
反复将待排序列建立成堆,并取堆顶。
八、归并排序
1. 算法思想
把一个有n个记录的无序文件看成是由n个长度为1的有序子文件组成的文件,然后进行两两归并,得到n/2个长度为2或1的有序文件,再两两归并,如此重复,直至最后形成包含n个记录的有序文件为止。
2. 排序过程示例
待排序列:39 19 32 25 46 58 47 55
初始状态(每个元素单独为一组):
[39] [19] [32] [25] [46] [58] [47] [55]
第1轮归并(两两归并):
[19 39] [25 32] [46 58] [47 55]
第2轮归并(四个一组):
[19 25 32 39] [46 47 55 58]
第3轮归并(全部归并):
[19 25 32 39 46 47 55 58]
3. 一句话总结
两两归并为一组,再四个记录归并为一组,依此类推。
九、算法复杂度分析
1. 时间复杂度表示
算法分析时常采用算法的时空开销随n的增长趋势来表示其时空复杂度。
表示方法:T(n) = O(f(n))
其中:
- n反映问题的规模
- T(n)是算法运行所消耗时间或存储空间的总量
- f(n)是自变量为n的某个具体的函数表达式
渐进分析:当n增大到一定值后,T(n)中影响最大的就是n的幂次最高的项,其他的常数项和低幂次项都可以忽略。
示例:
f(n) = n² + 2n + 1
则 T(n) = O(n²)
2. 常见复杂度等级
| 复杂度 | 名称 | 说明 |
|---|---|---|
| O(1) | 常数级 | 执行时间与问题规模无关 |
| O(n) | 线性级 | 执行时间与问题规模成正比 |
| O(log n) | 对数级 | 执行时间随问题规模对数增长 |
| O(n²) | 平方级 | 执行时间随问题规模平方增长 |
| O(n log n) | 线性和对数乘积 | 常见于高效排序算法 |
| O(n³) | 立方级 | 执行时间随问题规模立方增长 |
| O(2ⁿ) | 指数级 | 执行时间随问题规模指数增长 |
3. 复杂度增长趋势
O(1) < O(log n) < O(n) < O(n log n) < O(n²) < O(n³) < O(2ⁿ)
效率:高 → 低
十、排序方法总结与比较
1. 综合对比表
| 排序方法 | 最好时间 | 平均时间 | 最坏时间 | 辅助空间 | 稳定性 |
|---|---|---|---|---|---|
| 直接插入排序 | O(n) | O(n²) | O(n²) | O(1) | 稳定 |
| 简单选择排序 | O(n²) | O(n²) | O(n²) | O(1) | 不稳定 |
| 冒泡排序 | O(n) | O(n²) | O(n²) | O(1) | 稳定 |
| 希尔排序 | --- | O(n¹·²⁵) | --- | O(1) | 不稳定 |
| 快速排序 | O(n log₂n) | O(n log₂n) | O(n²) | O(log₂n)~O(n) | 不稳定 |
| 堆排序 | O(n log₂n) | O(n log₂n) | O(n log₂n) | O(1) | 不稳定 |
| 归并排序 | O(n log₂n) | O(n log₂n) | O(n log₂n) | O(n) | 稳定 |
2. 各排序算法特点
(1)直接插入排序
- 优点:简单、稳定、对基本有序的序列效率高
- 缺点:平均和最坏情况下时间复杂度高
- 适用:小规模数据或基本有序的数据
(2)冒泡排序
- 优点:简单、稳定
- 缺点:效率低
- 适用:教学演示、小规模数据
(3)简单选择排序
- 优点:简单、交换次数少
- 缺点:不稳定、效率低
- 适用:小规模数据
(4)希尔排序
- 优点:比直接插入排序快
- 缺点:不稳定、增量序列选择困难
- 适用:中等规模数据
(5)快速排序
- 优点:平均性能最好、应用广泛
- 缺点:不稳定、最坏情况差
- 适用:大规模随机数据
(6)堆排序
- 优点:空间复杂度O(1)、最坏情况也不错
- 缺点:不稳定
- 适用:大规模数据、对空间要求高
(7)归并排序
- 优点:稳定、性能好
- 缺点:需要额外空间O(n)
- 适用:大规模数据、外部排序
3. 稳定性说明
稳定排序:相等元素的相对顺序在排序后保持不变
- 直接插入排序
- 冒泡排序
- 归并排序
不稳定排序:相等元素的相对顺序可能改变
- 简单选择排序
- 希尔排序
- 快速排序
- 堆排序
十一、选择排序算法的建议
1. 根据数据规模选择
| 数据规模 | 推荐算法 |
|---|---|
| 小规模(n < 50) | 直接插入排序、简单选择排序 |
| 中等规模(50 < n < 1000) | 希尔排序、快速排序 |
| 大规模(n > 1000) | 快速排序、归并排序、堆排序 |
2. 根据数据特点选择
| 数据特点 | 推荐算法 |
|---|---|
| 基本有序 | 直接插入排序、冒泡排序 |
| 随机分布 | 快速排序、堆排序 |
| 大量重复 | 归并排序 |
| 需要稳定 | 归并排序、插入排序 |
3. 根据空间限制选择
| 空间限制 | 推荐算法 |
|---|---|
| 空间紧张 | 堆排序、希尔排序 |
| 空间充裕 | 归并排序、快速排序 |