最近复习数据结构,看完排序,就想着赶紧总结一下,供自己整理学习,也供大家一起学习进步。
目 录
一、概述
- 排序分为内排序和外排序。衡量算法的三个主要标准,空间复杂度、时间复杂度、稳定性(稳定性指关键字相同的元素,在排序后前后相对顺序不变,本身不含褒贬)。
- 内排序又分为插入排序、交换排序、选择排序、归并排序、基数排序(根据算法实现的思想不同)。
- 外排序主要采用归并排序,为提高外部排序的效率有三种相关的优化算法。
二、内排序
1.插入排序
插入排序包含:直接插入排序、折半插入排序、希尔排序
1)直接插入排序
算法思想:将数据集合看作是两个部分,前部分是有序集合,后半部分是待排序集合,每次从待排序的集合中取第一个元素,将其插入到有序集合应该放入的位置。在插入时候涉及到向后移位的操作,因为待插入位置之后的元素要向后移动,在直接插入排序中,比较和移位是同时进行的,待插入元素依次向前比较,若前一个元素大于它,则将前一个元素向后移动,再向前比较下一个元素,如图此时待排序元素为2,2与4比较,2小于4,在4前面,4向后移,2比较3,3向后移,2比较1,2在1后,则插入1之后。
void InsertSort(int array[],int len){
int i,j,key;
for(i=1;i<len;i++){
if(array[i]<array[i-1]){
key=array[i];
for(j=i-1;j>=0&&array[j]>key;j--){
array[j+1]=array[j];
}
array[j+1]=key;
}
}
}
空间复杂度:O(1);
时间复杂度:最好情况,序列已经是升序排列了,需要进行的比较操作需(n-1)次即可。最坏情况,序列是降序排列,那么此时需要进行的比较共有n(n-1)/2次。插入排序的赋值操作是比较操作的次数加上 (n-1)次。平均来说插入排序算法的时间复杂度为O(n^2);
稳定性:稳定;
使用范围:适用于顺序存储和链式存储。
总结:排序效率与初始序列的状态有关,适用于初始基本有序且规模不太大的序列,对于初始状态基本无序的序列,时间消耗较大。
2)折半插入排序(先折半查找再移动插入)
算法思想:直接插入排序是边比较边移动(如递增序列,若小于前一个数,前一个数向后移,继续向前比较),而折半插入排序则是将比较和移动进行分离。先折半查找元素待插入的位置,再统一移动待插入位置之后的所有元素。因此,折半插入排序主要是减少了元素比较的次数,但移动次数没变。
void BinaryInsertSort(int array[],int len){
int i,j,low,high,mid,key;
for(i=1;i<len;i++){
if(array[i]<array[i-1]){
key=array[i];
high=i-1;
low=0;
while(low<=high){
mid=(high+low)/2;
if(array[mid]>array[i])
high=mid-1;
else
low=mid+1;
}
for(j=i;j>low;j--){
array[j]=array[j-1];
}
array[low]=key;
}
}
}
空间复杂度:O(1);
时间复杂度:比较次数减小,平均为O(nlog2n),移动次数未减少,平均来说,算法的时间复杂度为O(n^2);
稳定性:稳定;
使用范围:适用于顺序存储。
总结:比较次数与待排序列初始状态无关,移动次数与初始序列状态有关,对于顺序存储的数据,使用折半插入可以节省比较的时间,但总的移动次数没有变。
3)希尔排序(缩小增量排序)
直接插入排序适用于数据量不大的排序,希尔排序适用于数据量比较大的情况。
算法思想:将数据分成若干组,假设a[10]内存有10个无序元素,取增量d为10/2=5,则将10个数据分为5组,每组2个元素,将下标为0,5(0+d)定义为1组,1,6(1+d)定义为2组,3,7(3+d)定义为3组,4,8(4+d)定义为4组,5,9(5+d)定义为5组(注意这里是说的下标),然后每组中的元素进行比较,如不符合排序规则就进行插入排序操作。
假设a[10]={8,5,3,1,2,4,6,9,7,0},递增排序;
第一轮增量为5,分组如下:
8,4为一组,4在8前,插入排序,4插入8前,则相当于两数互换,4,8;
5,6为一组,5在6前,插入排序,位置不变;
3,9为一组,3在9前,插入排序,位置不变;
1,7为一组,1在7前,插入排序,位置不变;
2,0为一组,0在2前,插入排序,0插入2前,相当于两数互换,0,2;
第二轮,取增量为d=5/2=2(此时5为第一次取的增量),分成2组,每组5个元素,下标0,2(0+d),4(0+2d),6(0+3d),8(0+4d)为第一组,下标1,3(1+d),5(1+2d),7(1+3d),9(1+4d)为第二组,分组情况如图。
此时执行插入排序,即{4,3,0,6,7}和{5,1,8,9,2}分别执行直接插入排序,直接插入排序过程就不赘述了(依次向前比较),比较结果插入相应位置,结果为{0,3,4,6,7}和{1,2,5,8,9},此时排序情况如图。
第三轮,取增量为d=2/2=1(2为第二轮所取的增量),分成1组,即全部10个元素执行直接插入排序,此时,因为元素经过上面两轮的排序,已经具有较好的局部有序性,则很快就能得到最终结果,直接插入排序不做赘述。
void ShellSort(int a[],int len){
int i,j,d=len/2,k