常用算法(4)-归并算法

来源:互联网 发布:淘宝店子账号怎么认证 编辑:程序博客网 时间:2024/06/01 09:50

形象视频:http://v.youku.com/v_show/id_XMzMyODk5Njg4.html

 

将两个有序的子序列[low,m]和[m,high]归并成一个有序的序列。合并过程中,设置i,j和p三个指针,其初值分别指向这三个记录区的起始位置。合并时以此比较arr[i]和arr[i],取较小的记录复制到arr1[p],然后将i或j,以及p均+1。

void MergeSort(int *arr,int low,int m,int high){    int i,j,p;    int *arr1;//缓存向量    i=low;    j=m+1;    p=0;    while(i<=m&&j<=high)//两序列非空时,将较小的输出到向量arr1中    {        if(arr[i]<arr[j])        {            arr1[p]=arr[i];            p++;            i++        }        else        {            arr1[p]=arr[j];            p++;            j++;        }    }    while(i<=m)//若该子序列非空,则将剩余的记录记录到arr1中    {        arr[p]=arr1[i];        p++;        i++    }    while(j<=high)//若该子序列非空,则将剩余的记录记录到arr1中    {        arr[p]=arr1[j];        p++;        j++;    }    for(i=low,p=0;i<=high;i++,p++)        arr[i]=arr1[p];}


1 自底向上的方法

基本思路:第一趟归并排序时,将待排序的arr[0...n]看作是 n个长度为1的有序子文件,将这些子序列两两归并,若n为偶数则得到n/2个长度为2的有序序列;若n为奇数,则最后一个子序列轮空(不参与归并),因此本次归并后得到钱lgn个有序子序列长度为2,但最后一个子序列长度仍为1。第二趟归并则将第一趟归并所得到lgn个有序子序列两两归并,如此反复,直到得到一个长度为n的有序序列为止。上述每次归并,均是将两个有序的子序列合并成一个有序子序列,故称其为“二路归并排序”

代码分析:在某趟归并中,设各子序列长度为length,则归并序列arr[0..n]中有序子序列:[1..length],[length+1....2length],.....,[n/length-1]*length+1.....n]。

在调用归并操作将相邻的一对子序列进行归并时,必须考虑两个问题:1子序列个数是奇数,2最后一各子序列长度小于length,对应的作如下处理,

1.若子序列个数为奇数,则最后一个子序列无须和其他子序列归并,即本趟轮空;

2.若子序列个数为偶数,要注意最后一对子序列中后一个序列的区间上界应该是n;

void MergePass(int *arr,int length){   int i;   //归并长度为length的两个相邻子序列   for(i=1;i+2*length-1<=n;i=i+2*length) MergeSort(arr,i,i+length-1,i+2*length-1);   //上述循环执行完后,arr数组中保存的是n/length个长度为length的有序子序列  //如果还剩下两个子序列,后者的长度小于length,则设置后者的区间上限为n   if(i+length-1<n)        MergeSort(arr,i,i+length-1,n)  //如果i<=n&&i+length-1>=n,也即n为奇数时,默认剩余的一个那个元素无须归并}//采用自底向上的方法,对arr[1....n]进行2路归并void Merge(int *arr){  int length;  for(length=1;length<n;length*=2)//作lgn趟归并 MergePass(arr,lenth);    }


2 自顶向下的方法【待研究】

 

    采用分治法进行自顶向下的算法设计,形式更为简洁。      设归并排序的当前区间是R[low..high],分治法的三个步骤是:①分解:将当前区间一分为二,即求分裂点                 ②求解:递归地对两个子区间R[low..mid]和R[mid+1..high]进行归并排序;③组合:将已排序的两个子区间R[low..mid]和R[mid+1..high]归并为一个有序的区间R[low..high]。   递归的终结条件:子区间长度为1(一个记录自然有序)。     void MergeSortDC(SeqList R,int low,int high)      {//用分治法对R[low..high]进行二路归并排序        int mid;        if(low<high){//区间长度大于1           mid=(low+high)/2; //分解           MergeSortDC(R,low,mid); //递归地对R[low..mid]排序           MergeSortDC(R,mid+1,high); //递归地对R[mid+1..high]排序           Merge(R,low,mid,high); //组合,将两个有序区归并为一个有序区         }      }//MergeSortDC


 时间复杂度:对长度为n的序列,需要进行lgn趟二路归并,每趟归并时间复杂度为O(n),所以好或坏的情况下时间复杂度均为O(nlgn)

空间复杂度:需要额外的向量保存结果,空间复杂度为O(n)

属于不稳定排序

 

参考:http://hi.baidu.com/sleet1986/blog/item/82f0ae8b6b4e38609f2fb4cd.html

原创粉丝点击