递归算法
将待排元素分成大小大致相同的两个子集合,分别对这两个集合进行排序,最终将排好序的子集合合并。
#include<iostream> #include<cstdio> void Merge(int s[],int t[],int b,int m,int e){//将s[b...m]与s[m+1...e]合并 int i=b;//i->前一个块 int j=m+1;//j->后一个块 int k=0;//k->t数组 while(i<=m&&j<=e){ if(s[i]<=s[j])t[k++]=s[i++]; else t[k++]=s[j++]; } while(i<=m){ t[k++]=s[i++]; } while(j<=e){ t[k++]=s[j++]; } for(int i=0;i<k;i++){ //每次都把序数组b的值赋回给a,这样a对于区间[b,e]也是有序的 s[b+i]=t[i]; } } void MSort(int s[],int t[],int b,int e){ if(b<e){ int m; m=(b+e)/2; MSort(s,t,b,m); MSort(s,t,m+1,e); Merge(s,t,b,m,e); } } int main(){ int a[10]={2,5,1,3,4,9,7,8,6,0}; int temp[10]; MSort(a,temp,0,9); for(int i=0;i<10;i++){ printf("%d ",a[i]); } printf("\n"); }
非递归算法
有三个函数:
(1)Merge函数,用于合并两个有序子段。这个函数主要是用于将a数组中[l,m]区间和[m+1,r]区间的数有序的合并到b数组的[l,r]区间。这里不同于递归方法里的Merge函数,b数组从l开始到r结束是有序的并且不用在Merge函数尾部将b数组的有序部分赋值给a数组(原因下面提到)。
(2)MergePass函数,这个函数的功能对于长为n数组进行归并,两个两个地合并长度为s的子段,当然我们的数组长度不可能每次正好满足“以s为子段长度时正好能两两合并,一个不多一个不少”。所以我们要对特殊情况进行判断:
while(i<=n-2*s){//剩下不超过2s长度的元素 Merge(x,y,i,i+s-1,i+2*s-1); i=i+2*s; }
在剩下的元素不能组成两个子段的时候分情况讨论:
a.当剩下元素可以凑成一个长为s子段,Merge(x,y,i,i+s-1,n-1);
b.当剩下元素连一个长为s的字段都凑不成时,我们直接将其略过(不排序)
(3)MergeSort函数,这个数组首先设一个临时数组b,与要归并的子段长度s,s初始为1,在s<n的前提下,我们每次调用MergePass函数,使得a数组中,以s为子段长度,每个子段内部都有序地排列,这次排好的序列体现在b数组上,a数组此时没变!然后将s+=s;可以开始两两合并之前长为s的子段啦!这次把b再合并到a上,所以一趟下来,最后a是“有序的”,这也是Merge函数里不用把数组b再赋给a的原因了,省了一趟赋值!
#include<iostream> #include<cstdio> #include<string> using namespace std; void Merge(int a[],int b[],int l,int m,int r){ int i=l,j=m+1,k=l; while(i<=m&&j<=r){ if(a[i]<a[j])b[k++]=a[i++]; else b[k++]=a[j++]; } while(i<=m)b[k++]=a[i++]; while(j<=r)b[k++]=a[j++]; } void MergePass(int x[],int y[],int s,int n){ int i=0; while(i<=n-2*s){//剩下不超过2s长度的元素 Merge(x,y,i,i+s-1,i+2*s-1); i=i+2*s; } if(i+s<n)Merge(x,y,i,i+s-1,n-1); else{ for(int j=i;j<=n-1;j++){ y[j]=x[j]; } } } void MergeSort(int a[],int n){ int b[10]; int s=1; while(s<n){ MergePass(a,b,s,n);//把a合并到b中 s+=s; MergePass(b,a,s,n);//把b合并到a中 s+=s; } } int main(){ int a[10]={2,5,1,3,4,9,7,8,6,0}; int n=10; MergeSort(a,n); for(int i=0;i<n;i++){ printf("%d ",a[i]); } printf("\n"); return 0; }
自然合并排序
自然合并排序:对于初始给定的数组,通常存在多个长度大于1的已自然排好序的子数组段.例如,若数组a中元素为{4,8,3,7,1,5,6,2},则自然排好序的子数组段有{4,8},{3,7},{1,5,6},{2}.用一次对数组a的线性扫描就足以找出所有这些排好序的子数组段.然后将相邻的排好序的子数组段两两合并,构成更大的排好序的子数组段({3,4,7,8},{1,2,5,6}).继续合并相邻排好序的子数组段,直至整个数组已排好序.
所以我们可以设变量flag表示序列中有几段已经排好序的子段。
设数组t[flag]表示每个有序子段的第一个元素所在的位置。
代码如下:
#include<iostream> #include<cstdio> #include<string> using namespace std; int t[10],flag=0; void Merge(int a[],int b[],int l,int m,int r){ int i=l,j=m+1,k=l; while(i<=m&&j<=r){ if(a[i]<a[j])b[k++]=a[i++]; else b[k++]=a[j++]; } while(i<=m)b[k++]=a[i++]; while(j<=r)b[k++]=a[j++]; } void getPos(int a[],int t[],int n){ t[flag++]=0; for(int i=0;i<n-1;i++){ if(a[i+1]!=a[i])t[flag++]=i+1; } } void MergePass(int x[],int y[],int s,int n){ int i=0; while(i<=flag-2*s){ int r=((i+2*s)<flag)?t[i+2*s]:n; Merge(x,y,t[i],t[i+s]-1,r-1); i=i+2*s; } if(i+s<flag)Merge(x,y,t[i],t[i+s]-1,n-1); else{ for(int j=t[i];j<=n-1;j++){ y[j]=x[j]; } } } void MergeSort(int a[],int n){ int b[10]; int s=1; while(s<flag){//最多flag段 MergePass(a,b,s,n);//把a合并到b中 s+=s; MergePass(b,a,s,n);//把b合并到a中 s+=s; } } int main(){ int a[10]={2,5,1,3,4,9,7,8,6,0}; int n=10; getPos(a,t,n); MergeSort(a,n); for(int i=0;i<n;i++){ printf("%d ",a[i]); } printf("\n"); return 0; }