内部排序与外部排序
内部排序是指在内存中进行的排序,数据量较小,可以一次性全部读入内存进行排序。 而外部排序是指数据量较大,无法一次性全部读入内存,需要将数据分成若干个小块,每次读入一部分数据进行排序,最后将排好序的小块合并成一个有序的大块。排序算法往往指的是内部排序算法,即数据记录在内存中进行排序。内部排序的过程是一个逐步扩大记录的有序序列长度的过程。
插入排序
直接插入
•排序过程 :先将序列中第1个记录看成是一个有序子序列,然后从第2个记录开始,逐个进行插入,直至整个序列有序。整个排序过程为n-1趟插入。
•算法描述
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 typedef struct { int key; float info; }JD; void straisort (JD r[],int n) { int i,j; for (i=2 ;i<=n;i++) { r[0 ]=r[i]; j=i-1 ; while (r[0 ].key<r[j].key) { r[j+1 ]=r[j]; j--; } r[j+1 ]=r[0 ]; } }
•算法评价
-时间复杂度: T ( n ) = O ( n 2 ) T(n)=O(n^2) T ( n ) = O ( n 2 )
»若待排序记录按关键字从小到大排列(正序)
关键字比较次数:
记录移动次数:
»若待排序记录按关键字从大到小排列(逆序)
关键字比较次数:
记录移动次数:
»若待排序记录是随机的,取平均值
关键字比较次数:
记录移动次数:
-空间复杂度 :S ( n ) = O ( 1 ) S(n)=O(1) S ( n ) = O ( 1 )
二分插入
也叫折半插入排序
•排序过程 :用折半查找方法确定插入位置的排序
•算法描述
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 void binsort (JD r[],int n) { int i,j,x,s,m,k; for (i=2 ;i<=n;i++) { r[0 ]=r[i]; x=r[i].key; s=1 ; j=i-1 ; while (s<=j) { m=(s+j)/2 ; if (x<r[m].key) j=m-1 ; else s=m+1 ; } for (k=i-1 ;k>=s;k--) r[k+1 ]=r[k]; r[s]=r[0 ]; } }
•算法评价
-时间复杂度 :T ( n ) = O ( n 2 ) T(n)=O(n^2) T ( n ) = O ( n 2 )
-空间复杂度 :S ( n ) = O ( 1 ) S(n)=O(1) S ( n ) = O ( 1 )
希尔排序
又称缩小增量法
•排序过程 :用折半查找方法确定插入位置的排序
•算法描述
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 void shellsort (JD r[],int n,int d[T]) { int i,j,k; JD x; k=0 ; while (k<T) { for (i=d[k]+1 ;i<=n;i++) { x=r[i]; j=i-d[k]; while ((j>0 )&&(x.key<r[j].key)) { r[j+d[k]]=r[j]; j=j-d[k]; } r[j+d[k]]=x; } k++; } }
•希尔排序特点
交换排序
冒泡排序
•排序过程
改进:上一趟排序记录最后一次交换的位置,作为下一趟排序的结束位置。
•算法描述
1 2 3 4 5 6 7 8 9 10 11 12 void BubbleSort (Elem R[ ], int n) { i = n; while (i >1 ) { lastExchangeIndex = 1 ; for (j = 1 ; j < i; j++) if (R[j].key > R[j+1 ].key) { Swap (R[j], R[j+1 ]); lastExchangeIndex = j; } i = lastExchangeIndex; } }
•算法评价
-时间复杂度 :T ( n ) = O ( n 2 ) T(n)=O(n^2) T ( n ) = O ( n 2 )
»最好情况(正序)
比较次数:n-1
移动次数:0
»最坏情况(逆序)
比较次数:
移动次数:
-空间复杂度 :S ( n ) = O ( 1 ) S(n)=O(1) S ( n ) = O ( 1 )
快速排序
•排序过程
•算法描述
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 void qksort (JD r[],int t,int w) { int i,j,k; JD x; if (t>=w) return ; i=t; j=w; x=r[i]; while (i<j) { while ((i<j)&&(r[j].key>=x.key)) j--; if (i<j) { r[i]=r[j]; i++; } while ((i<j)&&(r[i].key<=x.key)) i++; if (i<j) { r[j]=r[i]; j--; } } r[i]=x; qksort (r,t,j-1 ); qksort (r,j+1 ,w); }
•算法评价
-时间复杂度 :T ( n ) = O ( n 2 ) T(n)=O(n^2) T ( n ) = O ( n 2 )
»最好情况(每次总是选到中间值作划分元)T ( n ) = O ( n l o g 2 n ) T(n)=O(nlog_2n) T ( n ) = O ( n l o g 2 n )
»最坏情况(每次总是选到最小或最大元素作划分元)T ( n ) = O ( n 2 ) T(n)=O(n^2) T ( n ) = O ( n 2 )
-空间复杂度 :需栈空间以实现递归
»最坏情况:S ( n ) = O ( n ) S(n)=O(n) S ( n ) = O ( n )
»一般情况:S ( n ) = O ( l o g 2 n ) S(n)=O(log_2n) S ( n ) = O ( l o g 2 n )
常用“三者取中”法来选取划分记录,即取首记录r[s].key.尾记录r[t].key和中间记录r[(s+t)/2].key三者的中间值为划分记录。
选择排序
简单选择排序
•排序过程
首先通过n-1次关键字比较,从n个记录中找出关键字最小的记录,将它与第一个记录交换
再通过n-2次比较,从剩余的n-1个记录中找出关键字次小的记录,将它与第二个记录交换
重复上述操作,共进行n-1趟排序后,排序结束
•算法描述
1 2 3 4 5 6 7 8 9 10 11 12 13 14 void smp_selesort (JD r[],int n) { int i,j,k; JD x; for (i=1 ;i<n;i++) { k=i; for (j=i+1 ;j<=n;j++) if (r[j].key<r[k].key) k=j; if (i!=k) { x=r[i]; r[i]=r[k]; r[k]=x; } } }
•算法评价
-时间复杂度 : T ( n ) = O ( n 2 ) T(n)=O(n^2) T ( n ) = O ( n 2 )
»记录移动次数
Y最好情况:0
Y最坏情况:3(n-1)
»比较次数:
-空间复杂度 :S ( n ) = O ( 1 ) S(n)=O(1) S ( n ) = O ( 1 )
归并排序
2-路归并排序
•排序过程
设初始序列含有n个记录,则可看成n个有序的子序列,每个子序列长度为1
两两合并,得到n / 2 n/2 n / 2 下取整个长度为2或1的有序子序列
再两两合并,……如此重复,直至得到一个长度为n的有序序列为止
•算法描述
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 void merge (int SR[], int first,int mid, int last, int TR[]) { int i,j,k; i=first; j=mid+1 ;k=1 ; while ((i <= mid) && (j <= last)){ if (SR[i]>SR[j]){TR[k]=SR[j]; j=j+1 ; k=k+1 ;} else {TR[k]=SR[i]}; i=i+1 ; k=k+1 ;} } while (i<=mid){ TR[k]=SR[i]; i=i+1 ; k=k+1 ;} while (j<=last){ TR[k]=SR[j]; j=j+1 ; k=k+1 ;} } void mergesort (int SR[], int first, int last, int TR[]) { int temp[]; if (first < last) { mid = (first + last) / 2 ; mergesort (SR, first, mid, temp); mergesort (SR, mid + 1 , last, temp); merge (temp, first, mid, last, TR); } else { TR[first] = SR[first]; } }
•算法评价
-时间复杂度 :每一趟归并的时间复杂度为O ( n ) O(n) O ( n ) , 总共需要归并l o g 2 n log2n l o g 2 n 趟,因而,总的时间复杂度为O ( n l o g 2 n ) O(nlog2n) O ( n l o g 2 n ) 。
-空间复杂度 :2-路归并排序过程中,需要一个与表等长的存储单元数组空间,因此,空间复杂度为O ( n ) O(n) O ( n ) 。
排序方法比较
排序方法选择主要考虑
待排序记录的数量
关键字的分布情况
对排序稳定性要求
时间特性
时间复杂度为O ( n l o g n ) O(nlogn) O ( n l o g n ) :快速、归并排序
时间复杂度为O ( n 2 ) O(n^2) O ( n 2 ) :插入、冒泡、选择排序,插入最常用,尤其基本有序时,选择记录移动次数最少
当待排序记录基本有序时:插入和冒泡可达到O ( n ) O(n) O ( n ) ;
选择、归并排序的时间特性不随关键字分布而改变
对照表
排序方法
平均时间
最坏情况
最好情况
辅助空间
稳定性
直接插入排序(含二分)
O ( n 2 ) O(n^2) O ( n 2 )
O ( n 2 ) O(n^2) O ( n 2 )
O ( n ) O(n) O ( n )
O ( 1 ) O(1) O ( 1 )
√
希尔排序
很复杂,是增量d的函数
很复杂
很复杂
O ( 1 ) O(1) O ( 1 )
×
选择排序
O ( n 2 ) O(n^2) O ( n 2 )
O ( n 2 ) O(n^2) O ( n 2 )
O ( n 2 ) O(n^2) O ( n 2 )
O ( 1 ) O(1) O ( 1 )
×
冒泡排序
O ( n 2 ) O(n^2) O ( n 2 )
O ( n 2 ) O(n^2) O ( n 2 )
O ( n ) O(n) O ( n )
O ( 1 ) O(1) O ( 1 )
√
快速排序
O ( n l o g n ) O(nlogn) O ( n l o g n )
O ( n 2 ) O(n^2) O ( n 2 )
O ( n l o g n ) O(nlogn) O ( n l o g n )
O ( l o g n ) O(logn) O ( l o g n )
×
归并排序
O ( n l o g n ) O(nlogn) O ( n l o g n )
O ( n l o g n ) O(nlogn) O ( n l o g n )
O ( n l o g n ) O(nlogn) O ( n l o g n )
O ( n ) O(n) O ( n )
√