排序
方法模板
void X_Sort(ElementType[] A, int N)
- 大多数情况下,为简单起见,讨论从小到大的整数排序
- N是正整数,表示数组的长度
前提
- 只讨论基于比较的排序(例如通过 > = < 进行比较)
- 只讨论内部排序(所有数据加载入内存中)
- 稳定性:任意两个相等的数据,排序前后的相对位置不发生改变
- 没有一种排序是任何情况下都表现最好的
简单排序
冒泡排序
算法原理
从后往前开始遍历:
- 比较相邻的两个元素,如果第二个元素比第一个元素小,则进行交换
- 从开始一对到最后一对,对每一对相邻元素做2的工作。这步操作完成后,最后的元素应该是最大的元素
- 重复以上步骤,直到倒数第一个元素(最后一个元素不参与上述循环)
- 持续每次对越来越少的元素重复以上的步骤,直到没有任何一对元素需要比较
实现
void Bubble_Sort(ElementType[] A, int N) {
for (P = N - 1; P >= 0; P--) {
flag = 0; // 用于标识这趟冒泡过程是否进行了元素的交换
for (i = 0; i < P; i++) { // 一趟冒泡过程
if (A[i] > A[i + 1]) {
Swap(A[i], A[i + 1]);
flag = 1; // 标识元素进行了交换
}
}
if (flag == 0) // 全程无交换,说明数组已经有序了,不需要再进行排序了
break;
}
}
- 时间复杂度:
- 最好情况:已经顺序排好,
T=O(N) - 最坏情况:逆序排好,
T=O(N2)
- 最好情况:已经顺序排好,
- 稳定性:基于
A[i] > A[i + 1]
的判断方式,该冒泡排序是稳定的 - 冒泡排序可以处理单向链表的排序
插入排序
算法原理
从第二个元素遍历到最后一个元素:
1. 记录当前元素为temp
2. 从当前位置向前遍历,如果前一个元素大于temp
,则把下个元素后移,直到前一个个元素小于或等于temp
3. 把temp
置于空出来的位置
实现
void Insertion_Sort(ElementType[] A, int N) {
for (P = 1; P < N; P++) {
temp = A[P]; // 记录要比较的元素
for (i = P; i > 0 && A[i - 1] > temp; i--)
A[i] = A[i - 1]; // 向后移动元素,空出空位
A[i] = temp; // 元素置于空位
}
}
- 时间复杂度:
- 最好情况:已经顺序排好,
T=O(N) - 最坏情况:逆序排好,
T=O(N2)
- 最好情况:已经顺序排好,
- 稳定性:基于
A[i - 1] > temp
的判断方式,该插入排序是稳定的
时间复杂度下界
逆序对
对于下标
i<j ,如果A[i] > A[j]
,则称<i,j> 是一对逆序对(inversion)
- 定理:任意
N 个不同元素组成的序列平均具有N(N−1)/4 个逆序对
逆序对与排序
对于冒泡、插入排序:
- 交换2个相邻元素正好消去1个逆序对
- 有
n 个逆序对,则需要交换n 次元素 - 插入排序:
T(N,I)=O(N+I) ,N 是元素的个数,I 是逆序对的个数
- 如果序列基本有序,则插入排序简单且高效
对于排序:
- 定理:任何仅以交换相邻两元素来排序的算法,其平均时间复杂度为
Ω(N2) - 如果要提高算法效率,必须
- 每次消去不止1个逆序对
- 每次交换相隔较远的2个元素