排序1
排序相关名词
1. 排序算法的稳定性(Stability)
◼ 概念 : 如果相等的2个元素,在排序前后的相对位置保持不变,那么这是稳定的排序算法
- 排序前:5, 1, 3𝑎, 4, 7, 3𝑏
- 稳定的排序: 1, 3𝑎, 3𝑏, 4, 5, 7
- 不稳定的排序:1, 3𝑏, 3𝑎, 4, 5, 7
◼ 对自定义对象进行排序时,稳定性会影响最终的排序效果 (比如学生按成绩排序)
◼ 冒泡排序属于稳定的排序算法
- 稍有不慎,稳定的排序算法也能被写成不稳定的排序算法,比如下面的冒泡排序代码是不稳定的
2. 原地算法(In-place Algorithm)
概念 :
-
不依赖额外的资源或者依赖少数的额外资源,仅依靠输出来覆盖输入
-
空间复杂度为 𝑂(1) 的都可以认为是原地算法
◼ 非原地算法,称为 Not-in-place 或者 Out-of-place
3. 常见的递推式与复杂度
递推式 | 复杂度 |
---|---|
T(n) = T (n/2) + O(1) | O(logn) |
T(n) = T(n - 1) + O(1) | O(n) |
T(n) = T(n / 2) + O(n) | O(n) |
T(n) = 2 * T(n / 2) + O(1) | O(n) |
T(n) = 2 * T(n / 2) + O(n) | O(nlogn) |
T(n) = T(n - 1) + O(n) | O(n2) |
T(n) = 2 * T(n - 1) + O(1) | O(2n) |
T(n) = 2 * T(n - 1) + O(n) | O(2n) |
排序算法
10种排序算法
-
以上表格是基于数组进行排序的一般性结论
-
冒泡、选择、插入、归并、快速、希尔、堆排序,属于比较排序(Comparison Sorting)
1. 冒泡排序(Bubble Sort)
◼ 冒泡排序也叫做起泡排序
◼ 执行流程(统一以升序为例子)
- 从头开始比较每一对相邻元素,如果第1个比第2个大,就交换它们的位置
- 执行完一轮后,最末尾那个元素就是最大的元素
- 忽略 ① 中曾经找到的最大元素,重复执行步骤 ①,直到全部元素有序
◼ 时间复杂度最好是O(n) ----- 当数组完全有序时
◼ 时间复杂度最坏是O(n2) ----- 当数组为倒序时(每次都需要交换)
◼ 空间复杂度O(1)
代码实现:
◼ 冒泡排序优化
优化方法1 :
-
如果序列已经完全有序,可以提前终止冒泡排序
-
当某一次遍历不存在交换时 说明数组已经有序 直接退出
-
增加一个bool值,用于判断一次循环后是否有数据交换,如果没有,则退出排序
-
如果数据不是完全有序,此优化会因多了额外的指令而导致计算时间更长(多了判断)
代码实现:
优化方法2 :
- 如果序列尾部已经局部有序,可以记录最后1次交换的位置,减少比较次数
- 记录上一次循环最后一次交换的位置,将其作为下一次循环的截止位置
代码实现:
2. 选择排序(Selection Sort)
◼ 执行流程
-
从序列中找出最大的那个元素,然后与最末尾的元素交换位置
-
执行完一轮后,最末尾的那个元素就是最大的元素
-
忽略 ① 中曾经找到的最大元素,重复执行步骤 ①
代码实现:
- 选择排序的交换次数要远远少于冒泡排序,平均性能优于冒泡排序。
- 最好,最坏,平均时间复杂度:O(n2),空间复杂度:O(1)。
- 属于不稳定排序。
3. 堆排序(Heap Sort)
◼ 堆排序可以认为是对选择排序的一种优化 (利用二叉堆找到数组中的最大元素)
- 最好,最坏,平均时间复杂度:O(nlogn)。
- 空间复杂度O(1),属于不稳定排序。
◼ 执行流程
- 对序列进行原地建堆
(heapify)
构成一个大顶堆
- 重复执行以下操作,直到堆的元素数量为 1
-
交换堆顶元素与堆尾元素
-
堆的元素数量减 1
-
对 0 位置进行 1 次
siftDown
操作
代码实现:
4. 插入排序(Insertion Sort)
◼ 插入排序非常类似于扑克牌的排序
◼ 执行流程
① 在执行过程中,插入排序会将序列分为2部分
✓ 头部是已经排好序的,尾部是待排序的
② 从头开始扫描每一个元素
✓ 每当扫描到一个元素,就将它插入到头部合适的位置,使得头部数据依然保持有序
✓ 就是与前面的元素挨个比较 符合条件就交换 直到遇到不符合条件的为止
代码实现:
-
最坏、平均时间复杂度:O(n 2 )
-
最好时间复杂度:O(n)
-
空间复杂度:O(1)
-
属于稳定排序
逆序对(Inversion)
◼ 什么是逆序对?
- 数组 <2,3,8,6,1> 的逆序对为:
<2,1> < 3,1> <8,1> <8,6> <6,1>,共5个逆序对
◼ 插入排序的时间复杂度与逆序对的数量成正比关系
- 逆序对的数量越多,插入排序的时间复杂度越高(交换的次数越多)
◼ 当逆序对的数量极少时,插入排序的效率特别高
- 甚至速度比 O(nlogn) 级别的快速排序还要快
◼ 数据量不是特别大的时候,插入排序的效率也是非常好的
二分搜索(Binary Search)
- 概念
-
如何确定一个元素在数组中的位置?
-
如果是无序数组,从第0个位置开始遍历搜索,平均时间复杂度:O(n)
-
如果是有序数组,可以使用二分搜索,最坏时间复杂度:O(logn)
-
二分查找操作的数据集是一个有序的数据集
-
二分查找能应用于任何类型的数据,只要能将这些数据按照某种规则进行排序
-
当待搜索的集合是相对静态的数据集时,使用二分查找是最好的选择
-
二分搜索 – 思路
-
假设在 [begin, end) 范围内搜索某个元素 V,mid == (begin + end) / 2
-
如果 V < mid,去 [begin, mid) 范围内二分搜索
-
如果 V > mid,去 [mid + 1, end) 范围内二分搜索
-
如果 V == mid,直接返回 mid
-
-
二分搜索 – 实例
- 搜索 10
- 二分搜索 – 实现
- 如果存在多个重复的值,返回的值不确定。
插入排序优化
-
思路是将【交换】转为【挪动】 减少交换次数
① 先将待插入的元素备份
② 头部有序数据中比待插入元素大的,都朝尾部方向挪动1个位置
③ 将待插入元素放到最终的合适位置
代码实现:
-
第二种优化(二分搜索优化)(减少比较次数, 由O(n) -> O(logn))
◼ 在元素 v 的插入过程中,可以先二分搜索出合适的插入位置,然后再将元素 v 插入
◼ 要求二分搜索返回的插入位置:第1个大于 v 的元素位置
-
如果 v 是 5,返回 2
-
如果 v 是 1,返回 0
-
如果 v 是 15,返回 7
-
如果 v 是 8,返回 5
◼ 二分搜索优化 – 思路(找到元素 V 的插入位置)
-
假设在 [begin, end) 范围内搜索某个元素 v,mid == (begin + end) / 2
-
如果 v < m,去 [begin, mid) 范围内二分搜索
-
如果 v ≥ m,去 [mid + 1, end) 范围内二分搜索
◼ 二分搜索优化 – 实例
代码实现:
- 需要注意的是,使用了二分搜索后,只是减少了比较次数,但插入顺序的平均时间复杂度依旧是O(n2)。
__EOF__

本文链接:https://www.cnblogs.com/coderlts/p/13826772.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】博客园的心动:当一群程序员决定开源共建一个真诚相亲平台
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】Flutter适配HarmonyOS 5知识地图,实战解析+高频避坑指南
【推荐】凌霞软件回馈社区,携手博客园推出1Panel与Halo联合终身会员
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· C# 锁机制全景与高效实践:从 Monitor 到 .NET 9 全新 Lock
· 一则复杂 SQL 改写后有感
· golang中写个字符串遍历谁不会?且看我如何提升 50 倍
· C# 代码如何影响 CPU 缓存速度?
· 智能桌面机器人:使用 .NET 为树莓派开发 Wifi 配网功能
· 提升Avalonia UI质感,跨平台图标库选型实践
· 突发,CSDN 崩了!程序员们开始慌了?
· C# 中委托和事件的深度剖析与应用场景
· 一个基于 .NET 8 + Ant Design Blazor 开发的简洁现代后台管理框架
· AppBox拖拽设计增删改查用户界面