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. 根据空间限制选择

空间限制 推荐算法
空间紧张 堆排序、希尔排序
空间充裕 归并排序、快速排序
相关推荐
KAU的云实验台36 分钟前
单/多UAV、静/动态路径规划,基于PlatEMO平台的带约束多目标优化 本文核心内容:
算法·matlab·无人机
Liangwei Lin40 分钟前
洛谷 P1807 最长路
数据结构·算法
会编程的土豆1 小时前
【数据结构与算法】二叉树从建立开始
数据结构·c++·算法
_日拱一卒1 小时前
LeetCode:最大子数组和
数据结构·算法·leetcode
计算机安禾1 小时前
【数据结构与算法】第22篇:线索二叉树(Threaded Binary Tree)
c语言·开发语言·数据结构·学习·算法·链表·visual studio code
算法鑫探1 小时前
解密2025数字密码:数位统计之谜
c语言·数据结构·算法·新人首发
计算机安禾2 小时前
【数据结构与算法】第21篇:二叉树遍历的经典问题:由遍历序列重构二叉树
c语言·数据结构·学习·算法·重构·visual studio code·visual studio
信奥胡老师2 小时前
P1255 数楼梯
开发语言·数据结构·c++·学习·算法
爱睡懒觉的焦糖玛奇朵3 小时前
【工业级落地算法之人员摔倒检测算法详解】
人工智能·python·深度学习·神经网络·算法·yolo·目标检测
小辉同志3 小时前
78. 子集
算法·leetcode·深度优先