3.5 排序算法

一、排序算法概述

常见的排序算法包括:

  1. 直接插入排序
  2. 冒泡排序
  3. 简单选择排序
  4. 希尔排序
  5. 快速排序
  6. 堆排序
  7. 归并排序

二、直接插入排序

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,则:

  1. 首先从j所指位置起向前搜索,找到第一个关键字小于pivot的记录时将该记录向前移到i指示的位置
  2. 然后从i所指位置起向后搜索,找到第一个关键字大于pivot的记录时将该记录向后移到j所指位置
  3. 重复该过程直至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. 根据空间限制选择

空间限制 推荐算法
空间紧张 堆排序、希尔排序
空间充裕 归并排序、快速排序
相关推荐
一个努力编程人2 小时前
机器学习————GBDT算法
人工智能·算法·机器学习
深圳市恒星物联科技有限公司2 小时前
基于图像识别算法与积水传感器的积水监测预警技术方案
人工智能·算法
liu****2 小时前
1.反向迭代器实现思路
数据结构·c++·反向迭代器·vector·list
小美单片机2 小时前
Proteus8.9安装保姆级教程
c语言·c++·算法·51单片机·proteus·大一新生
white-persist2 小时前
【红队渗透】Cobalt Strike(CS)红队详细用法实战手册
java·网络·数据结构·python·算法·安全·web安全
Project_Observer2 小时前
任务条件布局规则如何帮助自动管理任务?
大数据·数据结构·人工智能·深度学习·机器学习·编辑器
舟舟亢亢3 小时前
算法总结—【动态规划一维、二维、状态压缩】
算法·动态规划
宵时待雨3 小时前
C++笔记归纳9:模板进阶
开发语言·数据结构·c++·笔记
重生之后端学习3 小时前
152. 乘积最大子数组
数据结构·算法·leetcode·职场和发展·动态规划