标红的这些个子序列段有一个特点(只标出了部分),那就是都参与合并排序的最后以此排序,切且它的元素数量一定为2n个。为什么一定参与 最后一次排序呢?因为元素量为2n个,所以这些个元素一定可以合并为一个子序列段(在合并排序中),并且他的元素量一定大于等于数组元素的一半,这也就意味着该序列会参与合并排序的最后一次合并。因为元素量是2n次方,所以在对这些个元素进行初始合并的时候,找到前后的两个子序列的首尾坐标变得很简单,且每次被合并的子序列的长度都是可以预支的。该条件可以这样来描述,用K来表示2n,N表示在标红区段中的子序列每次合并的长度的话,R表示每次循环中需要合并的子序列的个数的话。满足 K = N * R。但因为比较是两两进行的,不可能一个元素自己合并,所以实际应用时要改成: R = (K / R) / 2。也就是R不可能一开始分8个段。然后子序列的的坐标的确定的话,第一个子序列首从零开始,第二个为N,完成一次合并后,两个都加N,就可以表示下一次的两个需要合并的子序列了。 具体代码如下:
#include<stdio.h>
#include<stdlib.h>
#define less(A,B)A<B
#define coswap if(less(A,B)){Item t =A;A=B;B= t}voidtwosort(int a[],int b [],int N,int r)//这里的N为待每个数组内需要排序的元素个数,r为数组数,然后这两个数相乘等于总的数组长度 {//因为是自底向上,所以每次的比较元素个数起始就算2,但需要比较的部分是4次。即满足该条件:比较的元素个数乘以比较的部分是等于数组的总元素量。
int one =0;//表示第一个子序列的首元素坐标
int two =N;//表示第二个数组的首元素坐标
int thr =0;//给数组b做下标
int cno ;//第一个子序列的计数器
int cnt ;for(int i=0;i<r;i++){
cnt =0;
cno =0;printf("第%d次比较的坐标:%d %d \n\n",i+1,one,two);while(cnt<N&& cno<N){if(less(a[one],a[two])){
b[thr++]= a[one++];
cno++;}else{
b[thr++]= a[two++];
cnt++;}}if(cnt>=N)while(cno<N){
b[thr++]= a[one++];
cno++;}else{while(cnt<N){
b[thr++]= a[two++];
cnt++;}}
one = one+N;//需要注意的是每完成一次遍历,第一部分的数组的下标位移对应的比较元素的个数即是N;第二部分的数组元素也是如此
two = one +N;//无需像之前那样繁琐的传每个子序列的上下标。 for(int v=0;v<16;v++)printf(" %d ",b[v]);//这里是方便检验用的,可以删除。 printf("\n");}}voidtwofen(int a[],int b[],int K){
int r;for(int N=1;N<K;N*=2){
r =(K/N)/2;twosort(a,b,N,r);for(int i=0;i<16;i++)a[i]= b[i];//在最后一遍排序之前的b是有序的 }}
int main(){
int a[16]={1,5,3,7,2,6,4,9,11,10,22,15,16,13,25,19};
int b[17]={0};twofen(a,b,16);}
#include<stdio.h>
#include<stdlib.h>
#define key(A)A
#define less(A,B)A<B
#define Number 29
int sort(int a[],int b[],int First,int Fnum,int Last,int Lnum){
int i = First;while(Fnum>0&& Lnum >0){//一个序列中的元素取完之后就添加另一个序列元素了。if(less(a[First],a[Last])){
b[i++]= a[First++];
Fnum--;}else{
b[i++]= a[Last++];
Lnum--;}}if(Fnum>0){while(Fnum>0){
b[i++]= a[First++];
Fnum--;}}else{while(Lnum>0){
b[i++]= a[Last++];
Lnum--;}}}voidcopy(int a[],int b[],int position){
int i = position;
int j = i;while(b[j]){
a[i++]= b[j++];}}
int huafen(int a[],int b[],int N,int position)//N为数组中最大元素值的下标,position为开始扫的位置。 {
int First;//前一段数组的首坐标
int Last;//后一段数组的首坐标
int Fnum;//容纳第一段数组的元素量
int Lnum;//容纳后一段数组的元素量
int cnt =1;//计数器
int flag =0;//判断First是否赋值
int Llag =0;//判断First是否赋值
int segments =1;for(int i=position;i<N;i++){if(less(a[i],a[i+1])&& i !=N-1){
cnt++;}else{if(flag ==0&& i !=N-1){
First = i-cnt+1;//这里加1是因为cnt是从1开始的,而First和Last是从0开始的
Fnum = cnt;printf("\nFirst == %d Fnum == %d\n",First,Fnum);
flag =1;//表示已赋值
cnt =1;//重新开始计数
segments++;}else{
Last = i-cnt +1;
Lnum = cnt;printf("\nLast == %d Lnum == %d\n",Last,Lnum);
cnt =1;//重新计数
Llag =1;
segments++;}if(Llag ==1&& flag ==1){sort(a,b,First,Fnum,Last,Lnum);
flag =0;//排完序之后标志值设为零
Llag =0;copy(a,b,First);}}}return segments;}voidAddNature(int a[],int b[],int N,int position){
int k =huafen(a,b,N,position);//对数组进行第一遍合并的时候同时获取子序列段数,根据子序列段数可以知道需要循环多少遍数组
int bour;
int i=2;//用于输出时做数组下标
int Num =0;//记录需要多少次扫描
float segments = k;while(segments>1){
segments = segments/2;
bour = segments;if(segments > bour){//应对子序列段是奇数的情况 ,也容易出现特殊情况,就是前面的排好序列之后刚好比后一个子序列的元素小,无形之中就减少//了一次排列。
bour++;
segments = bour;}
Num++;}for(int i=0;i<Num-1;i++){huafen(a,b,N,position);}}//以上为改进过后的自然排序数组//以下为适用于2^N个元素排序的数组voidtwosort(int a[],int b[],int N,int r)//这里的N为待每个数组内需要排序的元素个数,r为数组数,然后这两个数相乘等于总的数组长度 {//因为是自底向上,所以每次的比较元素个数起始就算2,但需要比较的部分是4次。即满足该条件:比较的元素个数乘以比较的部分是等于数组的总元素量。
int one =0;
int two =N;
int thr =0;
int cno ;
int cnt ;for(int i=0;i<r;i++){
cnt =0;
cno =0;printf("第%d次比较的坐标:%d %d \n\n",i+1,one,two);while(cnt<N&& cno<N){if(less(a[one],a[two])){
b[thr++]= a[one++];
cno++;}else{
b[thr++]= a[two++];
cnt++;}}if(cnt>=N)while(cno<N){
b[thr++]= a[one++];
cno++;}else{while(cnt<N){
b[thr++]= a[two++];
cnt++;}}
one = one+N;//需要注意的是每完成一次遍历,第一部分的数组的下标位移对应的比较元素的个数即是N;第二部分的数组元素也是如此
two = one +N;//无需像之前那样繁琐的传每个子序列的上下标。 for(int v=0;v<16;v++)printf(" %d ",b[v]);printf("\n");}}voidtwofen(int a[],int b[],int K){
int r;for(int N=1;N<K;N*=2){
r =(K/N)/2;twosort(a,b,N,r);for(int i=0;i<16;i++)a[i]= b[i];//在最后一遍排序之前的b是有序的 }}
int main(){
int a[Number]={20,1,10,3,6,9,5,36,78,96,23,19,64,28,99,45,12,5,24,44,56,23,32,54,76,54,49,39,67};
int b[Number]={0};
int two =1;for(;two<Number;two = two*2){}
two = two /2;twofen(a,b,two);AddNature(a,b,Number,two);
int i=0;
int k=0;while(b[i])printf(" %d ",b[i++]);//因为a少了一次复制,所以这里我们把a,和b的位置对调一下 sort(b,a,0,two,two,Number - two);printf("\n");while(a[k])printf(" %d ",a[k++]);}