排序算法按照不同的原则可以分为不同的类。如根据排序的稳定性可以分为稳定排序和不稳定排序;根据排序过程所涉及到的存储器分为内部排序和外部排序等等。这里将根据排序采用的策略进行分类,可分为:插入排序、交换排序、选择排序、归并排序和基数排序等。
插入排序
插入排序中最简单和基础的是直接插入排序和希尔排序。
1.直接插入排序
基本思想:依次取出原始序列中元素,然后将其有序插入有序的序列中。
特征:设置监视哨,其作用一方面是在循环中监视下标是否越界,省略了设置循环判断条件;另一方面在进入循环之前可以作为缓冲区域,这样可以保证记录的后移不会失去原值。
code:时间复杂度O(n^2),空间复杂度O(1),稳定排序。
void InsertSort(NOde list[],int n){
int i,j;
for(i=1;i<n;i++){
list[0]=list[i]; //设置监视哨</span><span style="font-family:SimSun;font-size:14px;">list[0]</span><span style="font-size:18px;">
j=i-1;
while(list[0]<list[j]){//当前值小于前面的值,则记录后移,直到正确位置
list[j+1]=list[j];
j--;
}
list[j+1] = list[0];//找到正确位置后将list[0]的原记录值插入
}
}
2.希尔排序
基本思想:又称为缩小增量排序,是对直接插入排序的一种改进排序。其先将整个待排序列分割成若干子序列,分别对子序列进行直接插入排序,等到整个序列中的记录基本有序时,再对整个序列进行一次直接插入排序。
特征:其对增量序列的选择没有严格要求,一般而言,对于n个序列,可以选择分组数依次为d1=n/2,d2=d1/2,d3=d2/2,...,di=1。
code:时间复杂度O(nlog2(n))-O(n^2),不稳定排序。
<span style="font-size:14px;">void ShellSort(NOde list[],int n){
int i,j,d;
for(d=n/2;d>0;d=d/2){//初始增量为n/2,每次缩小增量为d/2
for(i=d+1;i<=n;i++){
list[0]=list[i]; //设置监视哨
j=i-d; //前后记录增量为d,而不是1
while(j>=0 && list[0]<list[j]){//当前值小于前面的值,则记录后移,直到正确位置
list[j+d]=list[j];
j-=d;
}
list[j+1] = list[0];//找到正确位置后将list[0]的原记录值插入
}
}
}</span>
交换排序
交换排序中最典型的两个是冒泡排序和快速排序。
1.冒泡排序
基本思想:冒泡排序是最直观的一种排序方法,其排序过程中,相邻的两个元素进行比较,若前面的值大于后边的值则进行交换,否则则不交换。或者是较大值的前移。
特征:设置中间变量,进行数据存储。
code:时间复杂度O(n^2),稳定排序。
<span style="font-size:14px;">void Bubble_sort(NOde list[],int n){
int i,j,temp;
for(i=0;i<n;i++){
temp=0; //交换标志变量,初始化为未交换
for(j=0;j<n;j++){
if(list[j]>list[j+1]){//前者大于后者则交换
list[0]=list[j+1];
list[j+1]=list[j];
list[j]=list[0];
temp=1;
}
}
if(temp==0) break; //未交换,排序结束
}
}</span>
2.快速排序
基本思想:快速排序是对冒泡的一种改进,不同之处在于冒泡每次的比较和交换都是在相邻的单元进行的,只能左移或右移一个单元。而快排比较和交换式从两端向中间进行,大大减少了比较和移动的次数。
特征:任取序列中的某个数据作为基准线,通过一次的排序,将原始序列分为两个子序列,左子序列小于或等于该基准线,右子序列大于或等于该基准线,然后一次循环,直到最后排成有序序列。
code:时间复杂度O(nlog2(n)),不稳定排序。
int Partition(NOde list[],int low,int high){
list[0]=list[low];
while(low<high){
while(low<high && list[high]>=list[0])//在high端寻找一个比list[low]小的记录放入low
--high;
list[low]=list[high];
while(low<high && list[high]<=list[0])//在low端寻找一个比list[low]大的记录放入high
++low;
list[high]=list[low];
}
list[low]=list[0];
return low;//返回基准线位置
}
void Quick_sort(NOde list[],int low,int high){
int loc;
if(low<high){
loc=Partition(list,low,high);//进行快排
Quick_sort(list,low,loc-1);//对前半区域进行划分
Quick_sort(list,loc+1,high);//对后半区域进行划分
}
}
选择排序
选择排序中分为简单选择排序和堆排序。
1.简单选择排序
基本思想:顾名思义,即首先选择最小的数据放在第一个位置,再选取次小的数据放在第二个位置,以此类推,直至选出n-1个为止。
特征:选取每次的最小值min。
code:时间复杂度O(n^2),不稳定排序。
<span style="font-size:14px;">void Select_sort(NOde list[],int n){
int i,j,min;
for(i=0;i<n;i++){
min=i;
for(j=i+1;j<=n;j++){//在i-n的范围内寻找
if(list[min]>list[j])
min=j;
if(min!=j){
list[0]=list[min];
list[min]=list[i];
list[i]=list[0];
}
}
}
}</span>
2.堆排序
基本思想:堆排序是利用堆特性进行排序的,根据其性质可知,堆的根为整个序列的最大值或最小值,因此将原始序列进行建堆,再对输出堆顶原始后进行维护,可以实现堆排序。
特征:建堆以及堆维护。
code:时间复杂度O(nlog2(n)),不稳定排序。
void Createtheap(NOde list[],int m,int n){
int i,j,flag;
i=m;j=2*i;
list[0]=list[i];
flag=0;
while(j<=n && flag!=1){//沿值较小的分支向下筛选
if(j<n && list[j]>list[j+1])//选取孩子中值较小的分支
j++;
if(list[0]<list[j])
flag=1;
else{//继续向下筛选
list[i]=list[j];
i=j;
j=2*i;
list[i]=list[0]
}
}
}
void Heap_sort(NOde list[],int n){
for(int i=n/2;i>=1;i--)//初始化堆
Createtheap(list,i,n);
for(int i=n;i>=1;i--){//list[1]为堆顶元素,可以设置循环输出
list[1]=list[i];//将堆尾元素移至堆顶
Createtheap(list,1,i);//整理堆
}
}
归并排序
归并排序可分为多路归并排序和二路归并排序。这里只对二路归并排序进行总结。
1.二路归并排序
基本思想:将原始序列分为若干子序列,先将每个子序列进行排序,再将已排序的子序列进行合并,得到完整排序序列。
特征:先将排序长度设置为1,然后两两归并为n/2个子序表,依次下去,直到长度为n的有序表。
code:时间复杂度O(nlog2(n)),空间复杂度O(n),稳定排序。
<span style="font-size:14px;">void Merge(NOde a[],NOde b[],int i,int m,int n){
int la,lb,lc;
la=i;lb=m+1;lc=i; //序列la,lb,lc的始点
while(la<=m && lb<=n){
if(a[la]<a[lb]) //有序合并
b[lc++] = a[la++];
else b[lc++] = a[lb++];
}
while(la<=m) //复制第一个序列中剩下的元素
b[lc++] = a[la++];
while(lb<=n) //复制第二个序列中剩下的元素
b[lc++] = a[lb++];
}
void MergPass(NOde list[],NOde A[],int n,int c){
int i=0,j;
while(i+2*c-1<=n-1){ //长度均为c的两个子序列合并为一个序列
Merge(list,A,i,i+c-1,i+2*c-1);
i+=2*c;
}
if(i+c-1<n) //长度不相等的两个子序列合并为一个序列
Merge(list,A,i,i+c-1,n-1);
else //只剩一个序列时直接复制到A中
for(j=i;j<=n-1;j++)
A[j]=list[j];
}
void Merge_sort(NOde list[],int n){
int c=1; //初始归并长度为1
NOde A[MAXN]; //需要一个辅助空间
while(c<n){
MergPass(list,A,n,C); //一次合并,结果写入A中
c*=2; //序列长度扩大一倍
MergPass(A,list,n,C); //再次合并,结果写入list中
c*=2;
}
}</span>
其它排序
更新ing...