一、排序算法的分类
1、稳定排序和不稳定排序
稳定排序:关键字相同的记录排序前和排序后的顺序不变。反之则为不稳定排序
稳定排序有:直接插入排序,折半插入,冒泡排序,直接选择排序,基数排序,归并排序
不稳定排序有:希尔排序,快速排序,堆排序
2、内排序和外排序
按排序时记录存储的位置划分。
内排序:只使用计算机内存存放待排序记录。包括插入排序、选择排序、交换排序、归并排序和基数排序。
内排序的时间复杂度:主要由比较次数和移动次数决定。
外排序:待排序数据量较大,需要借助外部设备存储。例如多路平衡归并和败者树构造法。
外排序的时间主要由三部分决定:预处理时间,内部合并时间,外存读写记录的时间。
3、静态排序和动态排序
按照记录的移动或者传送方式划分
静态排序:排序过程中是对记录本身进行物理的重排,须要移动记录。这种排序的记录一般存放在顺序表中。
动态排序:给每个记录增加一个链接域,通过修改链接改变记录的逻辑顺序,记录本身不移动。
二、待排序数据对象的存储结构
排序的算法与待排序数据的存储结构密切相关。常见的存储结构有三种:
顺序存储结构:须要移动记录。
链式存储结构:调整记录位置时只需要修改链接,无须移动记录。
地址向量结构文件:调整地址向量中元素的相对位置,无须移动记录。
三、插入排序
概念:插入排序是一种由初始空集合开始,不断将记录插入到合适的位置的排序。常用的插入排序方法有直接插入排序,折半插入排序和希尔排序。直接插入排序、折半插入排序是稳定的排序。希尔排序时不稳定的排序。
1、直接插入排序
基本思想:顺序地将待排序序列中的各个记录按照关键字的大小插入到已排序的序列的适当位置。
排序过程图示:其中 [ ] 表示已排序的记录,有下划线的关键字表示排序过程中该记录需要向后移动一个位置。
C++代码:
int * insertSort(int *a, int length) {
int nowInsert;
for (int i = 1; i < length; i++) { //从第二个元素开始插入
nowInsert = a[i];//保存本次插入的元素
int j;
for (j=i-1; j >= 0 && a[j] > nowInsert; j--) {//在已排序的序列中依次往前比较
a[j + 1] = a[j]; //记录后移
}
a[j+1] = nowInsert;//将本次需要插入的元素插入到相应的位置
}
return a;
}
空间复杂度:Ο(1) 只需要一个额外空间保存当前待插入的记录
时间复杂度:Ο(n^2)
最好的情况:记录原本按照递增顺序排列,第二层循环不会进入,一次排序中,记录比较一次,移动两次。
记录的总比较次数:n-1
记录的总移动次数(赋值):2(n-1)
最坏的情况:记录原本按照递减顺序排列。这时第i次排序的第二个for循环次数为i,关键字比较次数为i+1,记录的移动次数为i+2,整个排序过程来看:
2、折半插入排序
基本思想:直接插入排序在查找插入点方法上的优化,直接插入用的是顺序查找,折半插入用的是折半查找。
C++代码:
int * bianryInsertSort(int *a, int length) {
int nowInsert;
int low, high, i, j, m;
for (i = 1; i < length; i++) {
nowInsert = a[i];//保存本次插入的元素
low = 0; high = i - 1;
//折半查找插入点
while (low <= high) {
m = (low + high) / 2;
if (nowInsert < a[m]) {
high = m - 1;
}
else {
low = m + 1;
}
}
//记录后移
for (j = i-1; j >= high+1; j--) {
a[j + 1] = a[j];
}
a[j + 1] = nowInsert;//将本次需要插入的元素插入到相应的位置
}
return a;
}
空间复杂度:Ο(1)
时间复杂度:Ο(n^2),减少了查找插入点时的比较次数,并没有减少记录移动次数。时间复杂度变化不大。
3、希尔排序
基本思想:不断地把待排序的记录分为若干小组,对同一组内的记录进行排序,在分组时,始终保持当前组内的记录个数超过前面分组排序时组内的记录个数,希尔排序又称缩小增量排序。
希尔排序是对直接插入排序的改进。从直接插入排序可以看出,当记录个数较少或序列接近有序时,直接插入排序的效率较高。希尔排序就是基于这两点考虑的。一开始增量值大时组内记录个数少,后面增量值小时组内记录接近有序。
关于增量的取法:最后一次增量值必须为1,最简单可取d(i+1)=d(i)/2。
排序过程图示:
C++代码:
int * shellSort(int *a, int length,int d[],int number) {
//d为增量值数组,number为增量个数
int i, j, k, m, span;
int nowInsert;
for (m = 0; m < number; m++) {
span = d[m];//span记录当前增量值
for (k = 0; k < span; k++) {
//类似直接插入排序
for (i = k + span; i < length; i += span) {
nowInsert = a[i];
j = i - span;
while (j > -1 && nowInsert < a[j]) {
a[j + span] = a[j];
j -= span;
}
a[j + span] = nowInsert;
}
}
}
return a;
}
空间复杂度:Ο(1)
时间复杂度:希尔排序的时间复杂度有增量的取值情况和个数决定,理想情况下接近Ο(n(log_2n )^2)
希尔排序适用于中等规模的记录排序的情况。
相关文章
https://mp.csdn.net/postedit/87636287 十大排序算法对比与总结