根据耿国华老师的sopc课程总结(都以从小到大排序为例)
插入类排序
直接插入排序
将第i个记录直接插入到前面排好序的i-1个记录中。将第i个记录K_i顺次与前面i-1个记录比较,将所有大于K_i的记录向后移,当遇到一个小于或者等于K_i的记录时,该记录后必有一个空位,此时把K_i插入空位即可
完整的直接插入排序从i=2开始,将第一个记录记为已排好序的序列
//直接插入排序
void InsSort(int *num, int len){
if(num == NULL || len == 0){
return;
}
for(int i = 2; i < len; i ++){
num[0] = num[i]; //监视哨
int j = i - 1;
while(num[j] > num[0]){
num[j+1] = num[j]; //后移数字
j --;
}
num[j+1] = num[0]; //插入到空位中
}
}
从时间耗费角度来看,主要时间耗费在关键字比较和移动元素上
对于一趟排序:
最好:顺序,while循环执行1次,且不移动记录
最坏:逆序,while循环执行和移动记录都为i-1次
对整个排序:
最好:已排好序(顺序),总的比较次数为n-1,移动记录次数为2(n-1),每次只对待插记录移动两次
最坏:逆序排列,总比较次数为(n+2)(n-1)/2,即i从i=2到i=n的累加和,移动记录次数也达到最大(n+4)(n-1)/2,即i+1从i=2到i=n的累加和
则算法时间复杂度为O(n^2),空间复杂度为O(1)
折半插入排序
利用折半查找思想在有序排列的i-1个记录中确定应插入位置
void BinSort(int *num, int len){
int low, high, mid, x;
for(int i = 2; i < len; i ++){
x = num[i];
low = 1;
high = i - 1;
while(low <= high){
mid = (low + high) / 2;
if(x < num[mid]){
high = mid - 1;
}
else{
low = mid + 1;
}
}
for(int j = i-1; j >= low; j --){
num[j+1] = num[j]; //记录后移
}
num[low] = x;
}
}
每插入一个元素,需要比较的次数最大为折半查找树的深度,如插入第i个元素时,需要进行log2n,因此插入n-1个元素的平均比较次数为nlog2n
虽然折半插入排序与直接插入排序比较,改善了比较次数的数量级为O(nlog2n),但未改变移动元素的时间耗费,故其时间复杂度为O(n^2)
希尔排序
先将待排序记录序列分割成若干个“较稀疏”的子序列,分别进行插入排序
- 首先选定记录间的距离为d_i(i=1),在整个待排序记录序列中将所有间隔d_i的记录分成一组,进行组内直接插入排序
- 然后取i=I+1,记录间距离为d_i(d_i<d_i-1),在整个待排序记录序列中将所有间隔d_i的记录分成一组,进行组内直接插入排序
- 重复步骤2,直到d_i=1,此时只有一个子序列,对该序列进行直接插入排序,完成整个排序过程
void ShellInsert(int *num, int len, int delta){
int i, j;
for(i = 1+delta; i < len; i ++){
if(num[i] < num[i-delta]){
num[0] = num[i];
for(j = i-delta; j > 0 && num[0] < num[j]; j -= delta){
num[j+delta] = num[j];
}
num[j+delta] = num[0];
}
}
}
void ShellSort(int *num, int len, int *delta, int n){
for(int i = 0; i < n; i ++){
ShellInsert(num, len, delta[i]);
}
}
时间复杂度为O(n^1.5)