1. 直接插入排序
直接插入排序是一种最简单的排序方法,基本操作是将一条记录插入到已排好序的有序表中,从而得到一个新的、记录数量增1的有序表。
直接插入排序过程:
算法描述:
void insertSort(SqList &L) {
for(int i=2; i<=L.length; i++) {
if(L[i] < L[i-1]) { // 需将L[i]插入有序子表
L[0] = L[i]; // 将待插入的记录暂存在监视哨中
L[i] = L[i-1]; // r[i-1]后移
int j;
for(j=i-2; L[j] > L[0]; j--){ // 注意循环停止的条件是 第一个 L[j] > L[0]的时候, 该点及之前的全都不再向后移,L[0]放在L[j+1]的位置
L[j+1] = L[j];
}
L[j+1] = L[0];
}
}
}
算法分析:
时间复杂度 (O(n^2))
空间复杂度(O(1)) (只需一个监视哨)
算法特点:
(1)稳定排序
(2)也适用于链表结构
(3)更适用于初始记录基本有序(正序)的情况,当初始记录无序,n较大时,此算法复杂度较高,不宜采用
2. 折半插入排序
直接插入排序采用 顺序查找法查找当前记录在已排好序的序列中的插入位置,由查找算法可知,这个过程可采用“折半查找”来实现,由此进行的插入排序成为 折半插入排序。
算法描述:
void BInsertSort(SqList &L) {
for(int i=2; i<=L.length; i++) {
L[0] = L[i];
int low = 1, high = i-1;
while(low <= high) {
int mid = (low + high) / 2;
if(L[0] < mid) high = mid - 1;
else low = mid + 1; // 等于也包含在内(稳定排序);直接放着就可以,后面不用再对这一部分做处理,只处理high的部分
} // 注意之后的代码中如何利用low与high!
for(j = i-1; j >= high+1; j--) { // 只用到了high,high后面的全部后移一格
L[j+1] = L[j];
}
L[high+1] = L[0]; // 应用high
}
}
算法分析:
时间复杂度 (O(n^2)) (只减少了比较次数,记录的移动次数不变)
空间复杂度(O(1)) (只需一个监视哨)
算法特点:
(1)稳定排序
(2)因为进行了折半查找,所以只能用于顺序结构,不能用于链式结构
(3)适合初始记录无序,n较大的情况(why?)
3. 希尔排序
直接插入排序,当待排序序列个数较少,且待排序序列基本有序时,效率较高。
希尔排序基于以上两点,从“减少记录个数”和“序列基本有序”两方面对直接插入排序进行了改进。
希尔排序实质是分组插入的方法。其分组不是简单的“逐块分组”,二是将相隔某个“ 增量”的记录分成一组。
如图:
(1)第一趟取增量(d_1 = 5),所有间隔为5的记录分为1组,共5组,各组中分别进行直接插入排序。
(2)第二趟取增量(d_2 = 3),所有间隔为3的记录分为1组,共3组,各组中分别进行直接插入排序。
(3)第三趟取增量(d_3 = 1),对整个序列进行一趟直接插入排序,排序完成。
算法描述:
可通过改写直接插入排序,改写部分主要有:
(1)前后记录位置的增量是dk,而不是1;
(2)r[0]只是暂存单元,不是哨兵。当j<=0时,插入位置已找到。(?)
void shellSort(SqList &L, int dt[], int t) {
for (k=0; k<t; k++) {
shellInsert(L, dt[k]);
}
}
void shellInsert(SqList &L, int dk) {
for(int i=dk+1; i<=L.length; i++) {
if(L[i] < L[i-dk]) { // 需将L[i]插入有序子表
L[0] = L[i]; // 暂存在L[0]中,但不是监视哨
L[i] = L[i-1]; // r[i-1]后移
int j;
for(j=i-dk; j>0 && L[0]<L[j]; j-=dk){ // 直接插入排序,可走到监视哨,出现相等,自然不满足L[0]<L[j]条件,但有增量后无法这样判断退出,因此需限定j>0
L[j+dk] = L[j];
}
L[j+dk] = L[0];
}
}
}
算法分析:
时间复杂度:低于直接插入排序
空间复杂度:O(1)
算法特点:
(1)记录跳跃式移动导致排序方法是不稳定的
(2)只能用于顺序结构不能用于链式结构
(3)应使增量序列中的值没有除1之外的公因子,且最后一个增量值必须等于1
(4)记录总的比较次数和移动次数都比直接插入排序少,n越大,越明显。所以适合初始记录无序、n较大的情况