归并排序,其实质也就是分而治之(Divide and Conquer)+合而为一
分而治之:
1,对当前的这个序列,将其分成两个子序列(合理划分)
2,分别查看子序列的长度,如果小于等于1,那么退出
3,继续执行条件1
如上图:说白了分而治之在这里就是跟递归差不多,将复杂的问题归约的简单化
合而为一:
将当前有序的两个子序列合并成一个有序的序列的过程
如上分析,我们可以把每一个数都当做有序的一个子序列,然后进行合并
接下来考虑下如何将将二个有序数列(含有多个元素)合并。这个非常简单,只要比较二个数列的第一个数,谁小就先取谁,取了后就后移对应的这个数列中的标记。再进行比较两个子序列,如果有标记移到最后,那直接将另一个数列的数据依次取出即可
#include <stdio.h>
void merge(int * a, int ii, int * b, int jj, int * c) //待排的两个数组啊,b,分别含有的数的个数是ii和jj,c是待放数组
{
int i,j,k;
int ll;
i = j = k = 0;
while(i < ii && j < jj) //循环退出的条件是,a数组排完啦或者b数组排完啦
{
if(a[i] <= b[j])
{
c[k] = a[i]; k++; i++; //不断的往数组c里存储值
}
if(a[i] >= b[j])
{
c[k] = b[j]; k++; j++; //不断的往数组c里存储值
}
}
if(i >= ii) //如果a数组排完啦,就将b数组之后的元素整体放在c数组后面
{
while(j < jj)
{
c[k] = b[j]; k++; j++;
}
}
if(j >= jj)
{
while(i < ii)
{
c[k] = a[i]; k++; i++;
}
}
}
int main()
{
int i;
int a[4] = {2, 3, 5, 9}; //待排数组a,里面有4个元素
int b[5] = {1, 4, 6, 7, 8}; //待排数组b,里面有5个元素
int c[9] = {0};
merge(a, 4, b , 5, c);
for(i =0; i<9;i++)
{
printf("%d ", c[i]);
}
printf("\n");
return 0;
}
运算结果:
可以看出合并两个有序数列的效率挺高的,达到了O(N)
这样,我们就合并完两个有序数列
所以说:分而治之,就是将其划分为一个个小的有序序列,然后再不断的合并这些小的有序序列,直到所有的有序序列都合并完成
当时在写程序的时候,我碰到了以下的问题:
对于一个数组,我如何将其拆开为两个数组比较好呢???
1,我们要将其拆开为两个数组啊,那么肯定需要两个数组名
int a[8] = {2, 4,1, 7, 5, 0 , 3, 8};
int n = 8;
int *b;
b = a + n/2;
mergesort(a, b, n/2); //两个数组:a,b 其中b是指向a[4]的地址
我的本意是想用a,b两个数组将源数组a分成两个小的数组,但是这个貌似不合适递归,我们每一次还得重新定义b,将b指向一个合理的位置,哎,太麻烦了!!!
2,
int a[8] = {2, 4,1, 7, 5, 0 , 3, 8};
int n = 8;
mergesort(a, n/2 , a+n/2, n);
恩恩,这样貌似好多了,不用重新定义了,但是当我们真正写代码的时候就会发现,这样写,递归的时候好像找不到第一个子序列的下界,所以还是不好的,不行,还是得改进
3,
int a[8] = {2, 4,1, 7, 5, 0 , 3, 8};
int n = 8;
mergesort(a, 0,n/2 , a+n/2,n/2, n);
恩,这回应该好多了,第一个子序列(0-n/2),第二个子序列是(n/2 - n),可以满足我们的要求了,但是,参数有点太多了吧!!!
4,
int a[8] = {2, 4,1, 7, 5, 0 , 3, 8};
int n = 8;
mergesort(a, 0,n/2 , n);
哈哈,这回应该好啦,我们可以将原始的数组分为两个小的子序列
a(0 - n/2)
a(n/2 - n)
嗯嗯,这样就特别好啦,不仅看起来简介,主要是实现分而治之起来相对方便(递归)
#include <stdio.h>
void mers(int *a, int begin,int middle,int end)
{
int num = end - begin;
int num1 = middle - begin;
int num2 = end - middle;
int aa[num1];
int bb[num2];
int i, j=0, k=0;
int ii=0, jj=0, kk=0;
int c[num];
for(i = begin; i<end; i++)
{
if(i<middle){
aa[j] = a[i];
j++;
}
if(i>=middle){
bb[k] = a[i];
k++;
}
}
while(jj < j && kk < k)
{
if(aa[jj] <= bb[kk])
{
c[ii] = aa[jj]; ii++; jj++;
}
if(aa[jj] > bb[kk])
{
c[ii] = bb[kk]; ii++; kk++;
}
}
if(jj >= j)
{
while( kk < k)
{
c[ii] = bb[kk]; ii++; kk++;
}
}
if(kk >= k)
{
while( jj < j)
{
c[ii] = aa[jj]; ii++; jj++;
}
}
i = 0;
int BBegin = begin;
for(;i < num; i++,BBegin++)
a[BBegin] = c[i];
printf("%d \n", num);
for(i=begin;i<end;i++)
printf("%d ", a[i]);
printf("\n");
getchar();
}
void mergesort(int *a, int begin, int middle, int end)
{
if(begin <= middle && middle <= end){
if(middle - begin == 1)
{
printf("begin %d , middle:%d, end:%d\n", begin, middle, end);
mers(a, begin , middle, end);
}
if(begin == middle || middle == end)
return ;
mergesort(a, begin, (middle-begin)/2+begin , middle);
mergesort(a, middle, (end - middle)/2+middle , end);
if((middle - begin == 2) || (middle - begin == 4))
{
printf("begin %d , middle:%d, end:%d\n", begin, middle, end);
mers(a, begin , middle, end);
}
}
}
int main()
{
int i;
int a[8] = {2, 4,1, 7, 5, 0 , 3, 8};
int n = 8;
mergesort(a, 0 , n/2, n); //两个数组:a[0 - (n/2-1)], a[n/2 - n]
for(i = 0; i< 8; i++)
printf("%d ", a[i]);
printf("\n");
}
嗯嗯,这样我们的归并就完成了
归并排序的效率是比较高的,设数列长为N,将数列分开成小数列一共要logN步,每步都是一个合并有序数列的过程,时间复杂度可以记为O(N),故一共为O(N*logN)