一、排序基础概念
- 排序码:数据元素中用于排序的依据属性(如数值大小)。
- 有序表与无序表:元素按排序码有序排列的表称为有序表,否则为无序表。
- 稳定性:若排序后相同元素的相对位置不变,则算法是稳定的,否则不稳定。
- 时间复杂度:衡量算法执行效率的核心指标,常用大O表示法描述。
- 内排序与外排序:内排序在内存中完成,外排序需借助外部存储。
- 排序方法的稳定性是指在排序过程中是否改变相同元素的相对位置,若不改变则稳定,否则不稳定
二、插入排序
在有序序列中插入一个元素,保持序列有序,类似摸牌,每摸一张牌都插入到合适的位置,保持手中的牌有序
2.1 直接插入排序(稳定)
- 原理:将待排序元素插入已排序序列的合适位置。
- 步骤:
- 设置哨兵位(下标0),存放待插入元素。
- 从后向前扫描已排序序列,若元素大于哨兵,则向后移动。
- 找到插入位置后,放入哨兵元素。
- 特点:适合小规模数据,时间复杂度为
O(n²)。
void InsertSort( SqList &L){
int i,j;
for (i=2;i<=L.length;++i){
if(L.r[i].key < L.r[i-1].key){ // 若"<",需将L.r[i]插入有序子表
L.r[O]=L.r[i];// 复制为哨兵
for (j=i-1; L.r[0].key<L.r[j].key; --j){
L.r[j+ 1]=L.r[j];// 记录后移
}
L.r[j+1]=L.r[0];// 插入到正确位置
}
}
}
2.2 折半插入排序(稳定)
- 优化点:使用二分查找确定插入位置,减少比较次数。
- 时间复杂度:仍为
O(n²),但比较次数降低。
2.3 希尔排序(不稳定)
- 原理:按增量序列分组进行插入排序,逐步缩小增量。
- 增量序列示例:
5, 3, 1(最终增量必须为1)。 - 特点:时间复杂度
O(n^(1.3~2)),适合中等规模数据。 采用折半查找法查找插入位置,减少了比较次数,但移动次数没有改变
首先定义一个增量序列dk,比如5,3,1;循环增量序列,按每个增量值给表分块,间隔做插入排序,增量序列必须是递减的,最后一个值一定是1;
三、交换排序
3.1 冒泡排序(稳定)
- 原理:通过相邻元素交换,将最小元素“冒泡”到序列前端。
- 步骤:
- 从后向前遍历,若相邻元素逆序则交换。
- 每轮遍历确定一个最小元素的位置。
- 时间复杂度:
O(n²),适合教学或小规模数据。
3.2 快速排序(不稳定)
-
原理:分治法思想,通过基准值将序列划分为左右子序列。
-
步骤:
- 选择一个中间数(假设是下标1)放在数组的0下标(此时中间值原本的下标1就空了),两个指针low high分别指向1和最后一个下标值
- 从high开始往前找到一个比中间值小的,把值放在low的位置,然后从low开始往后找,找到一个比中间值大的,则放在high的位置
- low和high相等时,一趟排序结束,把0下标中间值放到此时high的位置
- 对第一趟排序分出来的两边再递归执行过程2 3,最终排序完成
-
特点:平均时间复杂度
O(n log n),数据越乱效率越高。
四、选择排序
4.1 直接选择排序(不稳定)
- 原理:每次从剩余元素中选择最小(或最大)元素放入已排序序列末尾。
- 步骤:
- 遍历未排序序列,找到最小值。
- 将最小值与未排序序列首元素交换。
- 时间复杂度:
O(n²),简单但效率低。
4.2 堆排序(不稳定)
- 堆的定义:完全二叉树,满足父节点值均大于(大根堆)或小于(小根堆)子节点。
- 步骤:
- 建堆:
- 用数组存放二叉树结点
- 从最后一个非叶子结点即(
n/2)的位置开始,一直到第一个结点依次调整 - 将以该元素为根的二叉树调整为堆
- 排序:以小根堆为例
- 输出堆顶元素,以最后一个元素代替
- 根与左右孩子比较,并与其中最小者交换位置,直到到达叶子结点
- 循环步骤1,2直到剩余两个元素,直接比较
- 建堆:
- 时间复杂度:
O(n log n),适合动态数据流。
五、归并排序(稳定)
5.1 二路归并排序
- 原理:分治法思想,将序列递归拆分为子序列,合并时排序。
- 步骤:
- 将序列均分为两部分,递归排序子序列。
- 合并两个有序子序列,依次比较元素并填充结果。
- 时间复杂度:
O(n log n),适合外部排序。
六、算法对比与总结
| 算法 | 时间复杂度(平均) | 稳定性 | 适用场景 |
|---|---|---|---|
| 直接插入排序 | O(n²) | 稳定 | 小规模或部分有序数据 |
| 快速排序 | O(n log n) | 不稳定 | 大规模乱序数据 |
| 堆排序 | O(n log n) | 不稳定 | 动态数据或内存受限场景 |
| 归并排序 | O(n log n) | 稳定 | 外部排序或链表排序 |
七、总结
排序算法是计算机科学的核心基础,不同场景需选择合适的算法。例如:
- 实时性要求高:优先选择快速排序。
- 数据规模小:直接插入排序或冒泡排序更简单。
- 稳定性必需:归并排序或直接插入排序。