归并算法实质是由两部分算法形成的:
第一部分是通过分治思想,通过把同一类的元素简化为n个子序列,每个子序列的长度为1
第二部分是通过两个子序列通过比大小的方式归并为一个子序列
我们可以先来看看两个数组如何归并为一个有顺序的数组
#include <stdio.h> int main() { int i,j,L,p; int max[10]; int num[5]={2,4,6,8,9}; int ber[5]={3,4,5,7,8}; for(i=0,j=0,p=0;i<=4&&j<=4;p++) { if(num[j]>ber[i]) max[p]=ber[i++]; else max[p]=num[j++]; } if(i<=4) { for(L=0;L<=4-i;L++) max[p+L]=ber[i+L]; }else { for(L=0;L<=4-j;L++) max[p+L]=num[j+L]; } for(i=0;i<10;i++) { printf("%d\n",max[i]); } return 0; }
如上图所示便是两个数组比大小然后归并的过程,请注意以下几点:
(1)是两个数组的范围问题:也就是说如果其中一个数组内的元素全部排完则两数组比较结束,如上所示范围是i<=4
(2)是剩余元素加入的范围问题,设置了个变量L,从而使得剩余元素例如从num[i]到num[5]加入到max中
(3)是采用此方法的前提必须是两个数组都是已排序(遵从从小到大的顺序)的状态!!!!
分治思想的运用一般采用递归的方式进行
/*r[]代表原来本身的数组,s[]用于储存排序完成的数组,*/
/*left代表左值,right代表右值*/
int merge_sort(int r[],int s[],int left,int right)
{
int mid;
int t[20]; /*t[20]作为一个临时储存数组,用于储存子序列的元素*/
if(left==right) /*此处为递归结束条件*/
s[left]=r[right];
else
{
mid=(left+right)/2;
merge_sort(r,t,left,mid);
merge_sort(r,t,mid+1,right);
merge(t,s,left,mid,right);/*将t(left,mid)与t(mid+1,right)归并为s[]*/
}
return 0;
}
递归的进行离不开对递归结束的判断以及分治的运行:
(1)通过if……else来判断递归的结束
(2)其次是merge_sort的本身:
r[ ]数组是原数组,即被操作数组,通过被mid不断的划分,直至变为长度为1的子序列(上图if语句处,然后赋值给t[ ](临时数组),
(3)由于递归函数本身的顺序,merge函数运行时,开始逐步归并(从子序列为1,反向进行上述分治操作) 这保证了比较的数组都符合从小到大的顺序,进行第一部分操作
下面是一个数组的分治对比,可以类比上面两个数组的对比
void merge(int SR[],int TR[],int i,int m,int,n)
{
int j,k,l;
for(j=m+1,k=i;j<n&&i<m;k++)
{
if(SR[i]<SR[j])
{
TR[k]=SR[i++];
else
TR[k]=SR[j++];
}
if(i<=m)
{
for(l=0;l<=m-i;l++)
{
TR[k+l]=SR[j+l];
}
}else
{
for(l=0;l<=n-j;l++)
{
TR[k+l]=SR[j+l];
}
}
}