排序——归并排序(分治法)
Posted LT.C#
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了排序——归并排序(分治法)相关的知识,希望对你有一定的参考价值。
在写归并排序之前,首先谈一谈分治的思想,
所谓分治:就是将一个比较复杂的问题分解为若干个规模较小的但是类似原问题的子问题,(解决)递归地求解这些这些子问题,再合并这些子问题的解来求解原问题
可分为三步:
分解:将原问题分解为若干个规模较小的但是类似原问题的子问题
求解:递归地求解这些这些子问题,然而若子问题规模足够小,可直接求解
合并:合并这些子问题的解来求解原问题
接下来,终于可以写归并排序了
分解:将一个有N个数的数列分解为2个分别有n/2个数的2个子数列
求解:使用归并排序递归地分别排序两个子数列;
合并:将两个子数列合并并排好序
根据上面的思路,归并排序的思路如下
分解:N可能为奇数可能为偶数,要充分考虑到,mergesort函数如下
void mergeSort(int* A,int p,int r) { if(p<r) { int q=(p+r)/2; mergeSort(A,p,q); mergeSort(A,q+1,r); merge(A,p,q,r); } }
注意,要考虑到p=r(p为一个数组中将要排序的起始项下标,r为一个数组中将要排序的终止项下标),若p=r,则根本不需要排序
,接下来,就是负责合并两个有序数列(注意:这里是一个循环不变式,先假设两个子数列都以排好序,起始:只有一个数,已排好序,不变:合并排序函数merge保证他们排好序,终止:递归结束),这里有两种方法:哨兵法和非哨兵法
哨兵法:所谓哨兵就是在L数列和R数列尾部分别添加一个近乎无穷大的数防止在判断L[i]和[j]时越界
int n1=q-p+1; int n2=r-q; int *L=(int*)malloc((n1+1)*sizeof(int)); int *R=(int*)malloc((n2+1)*sizeof(int)); int t,tmp1=0,tmp2=0; for(t=p;t<=q;t++) { *(L+tmp1)=*(A+t); tmp1++; } for(t=q+1;t<=r;t++) { *(R+tmp2)=*(A+t); tmp2++; } *(L+n1)=ShaoBing; *(R+n2)=ShaoBing; int i=0,j=0; int k; /* 依次从两个数字中取元素,将小的放入原数组中 */ for(k=p;k<=r;k++) { if(*(L+i)<=*(R+j)) { *(A+k)=*(L+i); i++; } else { *(A+k)=*(R+j); j++; } } free(L); free(R);
非哨兵法:没有哨兵防止越界,就只能分 int n1=q-p+1 int n2=r-q int *L=(int*)malloc((n1+1)*sizeof(int)); int *R=(int*)malloc((n2+1)*sizeof(int));
int t,tmp1=0,tmp2=0; for(t=p;t<=q;t++) { *(L+tmp1)=*(A+t); tmp1++; } for(t=q+1;t<=r;t++) { *(R+tmp2)=*(A+t); tmp2++; } *(L+n1)=ShaoBing; *(R+n2)=ShaoBing; int i=0,j=0; int k; /* 依次从两个数字中取元素,将小的放入原数组中 */ for(k=p;k<=r;k++) {
if(i==n1)
{
while(j<n2)
{
*(A+k)=*(R+j);
j++;
k++;
}
break;
}
if(j==n2)
{
while(i<n1)
{
*(A+k)=*(L+i);
i++;
k++;
}
break;
}
if(*(L+i)<=*(R+j)) { *(A+k)=*(L+i); i++; } else { *(A+k)=*(R+j); j++; } } free(L); free(R);
以上是我的思考路程,附上一篇博文,归并写得比较好,参考
以上是关于排序——归并排序(分治法)的主要内容,如果未能解决你的问题,请参考以下文章