适用:考研408数据结构
章节:第8章 排序
目标:先搞懂"每种排序到底在干什么",重点掌握408最常考的一趟结果、时间复杂度、稳定性、适用场景。
一、排序这一章先抓住什么
408排序题基本绕不开四件事:
1. 排序过程:一趟之后序列变成什么
2. 性质判断:稳定 / 不稳定,原地 / 非原地
3. 复杂度:最好、平均、最坏
4. 场景选择:基本有序、数据量大、外存、链表等该选谁
先不要急着背代码,先把每个算法的"动作特征"记住。
二、插入排序
1. 直接插入排序
核心思想:
左边始终保持有序,
每次从右边拿一个元素,
插入到左边正确位置。
例如:
初始:5 2 4 6 1 3
第一趟把2插入到5前:
2 5 4 6 1 3
第二趟把4插入到2、5之间:
2 4 5 6 1 3
408秒杀特征
看到:
前面一段已经有序
下一个元素向前移动插入
就是直接插入排序。
性质
最好:O(n) ------ 原序列基本有序
平均:O(n^2)
最坏:O(n^2) ------ 原序列逆序
空间:O(1)
稳定:是
特别适合
数据量小
序列基本有序
2. 折半插入排序
和直接插入排序本质一样,只是:
用折半查找寻找插入位置
所以它减少的是:
比较次数
但是元素仍然要整体移动,因此:
移动次数仍可能是O(n^2)
结论
比较次数下降
整体时间复杂度仍是O(n^2)
稳定
408常见陷阱:
折半插入排序用了二分查找,所以时间复杂度是O(n log n)。
错误。
原因:
找位置快了
移动元素仍然慢
3. 希尔排序
核心思想:
先让相距较远的元素进行插入排序,
逐渐缩小间隔gap,
最后gap=1时再做一次插入排序。
可以理解成:
把序列先"粗略排顺"
再慢慢变成真正有序
性质
空间:O(1)
稳定:否
时间复杂度:依赖增量序列
408里不要强行背一个唯一复杂度。
重点记:
希尔排序不稳定
属于插入排序的改进
增量最后必须为1
三、交换排序
1. 冒泡排序
核心思想:
相邻元素两两比较,
逆序就交换,
一趟把一个最大元素"冒"到最后。
升序时第一趟:
最大的元素到最右边
第二趟:
次大的元素到倒数第二个位置
性质
带"本趟是否发生交换"的标志位时:
最好:O(n)
平均:O(n^2)
最坏:O(n^2)
空间:O(1)
稳定:是
408识别点
如果题目说:
一趟后最大元素一定归位
优先想到冒泡排序。
2. 快速排序
快速排序是408高频重点。
核心思想:
选一个枢轴pivot,
通过一趟划分,
让pivot左边都不大于它,
右边都不小于它,
此时pivot最终位置确定。
然后:
递归处理左区间和右区间
一趟划分最重要的结论
一趟划分结束后:
pivot的位置已经最终确定
但:
pivot左边内部不一定有序
pivot右边内部也不一定有序
性质
最好:O(n log n)
平均:O(n log n)
最坏:O(n^2)
递归栈:
平均 O(log n)
最坏 O(n)
稳定:否
什么时候最坏
如果固定选第一个元素作为pivot,而序列已经:
基本有序
或
完全逆序
可能每次都只能划出:
0个元素 + n-1个元素
于是退化成:
O(n^2)
什么时候最好
每次pivot都基本把序列平均分成两半:
T(n) ≈ 2T(n/2) + O(n)
所以:
O(n log n)
408秒杀口诀
快排一趟:
枢轴归位
左右不一定有序
四、选择排序
1. 简单选择排序
核心思想:
每一趟从未排序区间中找最小值,
放到当前最前面。
例如:
5 2 4 1 3
第一趟找最小1:
1 2 4 5 3
第二趟再从后面找最小2。
性质
无论原序列是否有序:
比较次数基本不变
总比较次数:
n(n-1)/2
所以:
最好:O(n^2)
平均:O(n^2)
最坏:O(n^2)
空间:O(1)
稳定:否
特点
它的交换次数很少:
最多n-1次
408会考:
哪种O(n^2)排序比较次数不受初始序列影响?
优先想到:
简单选择排序
2. 堆排序
堆排序也是408重点。
升序排列一般建立:
大根堆
原因:
堆顶最大
每次把最大值交换到最后
然后缩小堆范围,再调整。
两个核心动作
1. 建堆
2. 删除堆顶 + 向下调整
复杂度
建堆:O(n)
每次调整:O(log n)
共做约n次
总时间:O(n log n)
空间:O(1)
稳定:否
而且:
最好、平均、最坏都为O(n log n)
顺序存储下标
若从1开始编号:
结点i的左孩子:2i
结点i的右孩子:2i+1
父结点:floor(i/2)
408高频
升序 -> 大根堆
降序 -> 小根堆
不要记反。
五、归并排序
二路归并排序
核心思想:
先把两个有序子序列合成一个更大的有序序列
不断两两合并:
1个元素
-> 2个元素有序段
-> 4个元素有序段
-> 8个元素有序段
...
一次二路归并
例如:
左:2 5 8
右:1 4 9
用两个指针比较:
1 2 4 5 8 9
性质
最好:O(n log n)
平均:O(n log n)
最坏:O(n log n)
额外空间:O(n)
稳定:是
适合
链表
外部排序
需要稳定排序
408高频点
若有n个元素,二路归并趟数:
ceil(log2 n)
六、基数排序
基数排序不靠关键字之间直接比较。
例如三位十进制数:
329
457
657
839
436
720
355
LSD法:
先按个位分配收集
再按十位
最后按百位
每一位都使用桶。
时间复杂度
设:
n:元素个数
d:关键字位数
r:每一位可能的取值数
则:
O(d(n+r))
空间:
O(n+r)
稳定性:
通常稳定
408重点
基数排序:
不是比较排序
所以它不受比较排序:
Ω(n log n)
这个下界限制。
七、九种算法先建立"动作记忆"
直接插入:拿一个,向前插
折半插入:二分找位置,再移动
希尔:分组插入,gap逐渐减小
冒泡:相邻交换,大的往后冒
快速:pivot划左右,pivot归位
简单选择:每趟找最小放前面
堆排序:堆顶最大,交换到末尾
归并:两个有序段合成一个
基数:按个位、十位、百位分桶
八、408常见"看一趟结果猜算法"
如果一趟后:
最大元素一定在最后
可能:
冒泡
如果一趟后:
某个pivot左边都更小
右边都更大
pivot最终位置确定
就是:
快速排序
如果一趟后:
第一个位置放上全局最小元素
可能:
简单选择排序
如果前面一段始终保持有序
可能:
直接插入排序
如果序列呈现"每隔gap一组分别有序"
可能:
希尔排序
九、这一篇需要你掌握到什么程度
至少做到:
看到算法名
-> 知道一趟在干什么
看到中间序列
-> 大概能判断是哪种排序
看到复杂度/稳定性选项
-> 能快速排除明显错误
下一篇专门整理:
所有排序算法复杂度
稳定性
空间复杂度
初始序列影响
场景选择
外部排序
408选择题秒杀表