归并排序 (有迹可循)

归并:将两个或两个以上的有序表组合成一个新有序表。

基本思路: 

归并排序采用了分治策略(divide-and-conquer),就是将原问题分解为一些规模较小的相似子问题,然后递归解决这些子问题,最后合并其结果作为原问题的解。(也就是将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。)

 先递归的把数组划分为两个子数组,一直递归到数组中只有一个元素,然后再调用函数把两个子数组排好序,因为该函数在递归划分数组时会被压入栈,所以这个函数真正的作用是对两个有序的子数组进行排序;


基本步骤
就是将数组分成二组A,B,如果这两组组内的数据都是有序的,那么就可以很方便的将这两组数据进行排序。如何让这二组组内数据有序了?

可以将A,B组各自再分成两组。依次类推,当分出来的小组只有一个数据时(即为该层递归的返回条件,此时first=last),可以认为这个小组组内已经达到了有序,然后再合并相邻的两个小组就可以了。这样通过先递归的分解数列,再合并数列就完成归并排序。


首先考虑下如何将将两个有序数列合并。这个非常简单,只要从比较两个数列的第一个数,谁小就先取谁,取了后就在对应数列中删除这个数。然后再进行比较,如果有数列为空,那直接将另一个数列的数据依次取出即可。

//将有序数组a[]和b[]合并到c[]中  
void MemeryArray(int a[], int n, int b[], int m, int c[])  
{  
    int i, j, k;  
  
    i = j = k = 0;  
    while (i < n && j < m)  
    {  
        if (a[i] < b[j])  
            c[k++] = a[i++];  
        else  
            c[k++] = b[j++];   
    }  
  
    while (i < n)  
        c[k++] = a[i++];  
  
    while (j < m)  
        c[k++] = b[j++];  
}  

时间复杂度为O(n)


再看归并的操作:先递归分解数列,再对数列合并排序,

每层递归的返回条件就是last=first,即此分组中只有一个元素  

//将有两个有序数列a[first...mid]和a[mid...last]合并。  
void mergearray(int a[], int first, int mid, int last, int temp[])  
{
	int i=first, j=mid+1;
	int m=mid, n=last;
	int k=0;
	while(i<=m&&j<=n)
	{
		if(a[i]<a[j])
			temp[k++]=a[i++];
		else
			temp[k++]=a[j++];	
	}	
	while(i<=m)
		temp[k++]=a[i++];
	while(j<=n)
		temp[k++]=a[j++];
		
	for(i=0;i<k;i++)
		a[first+i]=temp[i];				
} 

void mergesort(int a[], int first, int last, int temp[])  
{
	if(first<last)
	{
		int mid=(first+last)/2;
		mergesort(a,first,mid,temp);    //左边有序 
		mergesort(a,mid+1,last,temp);	//右边有序 
		mergearray(a,first,mid,last,temp);     	//将两个有序数列合并 
	}
}


复杂度

时间复杂度

归并排序的过程图与完全二叉树十分相似,算法中把记录扫描一遍耗费O(n),

又由完全二叉树的深度知:整个归并排序需要[log2 n]次,故总的时间复杂度为O(n * log n)

换句话说,设数列长n,将数列分为小数列需要log n次,每一步都在合并有序数列,时间复杂度为O(n),

故总的时间复杂度为O(n * log n)  (同时间复杂度的还有希尔排序,堆排序,快速排序,归并排序效率较高)

空间复杂度

归并排序在归并过程中需要与原始记录序列同等数量的存储空间存放归并结果以及递归时深度为log2 n的栈空间,

故空间复杂度O(n + log n)


稳定性

mergearray()

该函数中有if(a[i]<a[j])之类的语句,说明其为两两比较,无跳跃情况,所以是稳定的算法


综上,归并排序是一种比较占用内存,但效率高且稳定的算法



测试代码:

#include<stdio.h>
#include<stdlib.h>

//归并排序

//将两个有序数列a[first...mid]和a[mid...last]合并。  
void mergearray(int a[], int first, int mid, int last, int temp[])  
{
	int i=first, j=mid+1;
	int m=mid, n=last;
	int k=0;
	while(i<=m&&j<=n)
	{
		if(a[i]<a[j])
			temp[k++]=a[i++];
		else
			temp[k++]=a[j++];	
	}	
	while(i<=m)
		temp[k++]=a[i++];
	while(j<=n)
		temp[k++]=a[j++];
		
	for(i=0;i<k;i++)
		a[first+i]=temp[i];				
} 

void mergesort(int a[], int first, int last, int temp[])  
{
	if(first<last)
	{
		int mid=(first+last)/2;
		mergesort(a,first,mid,temp);    //左边有序 
		mergesort(a,mid+1,last,temp);	//右边有序 
		mergearray(a,first,mid,last,temp);     	//将两个有序数列合并 
	}
}
int main()
{
    int a[] = {2,23,34,43,45,6,7,8,5,4,56,78,80,211,222,444,111};
    int length = sizeof(a)/sizeof(a[0]);        //sizeof(a)计算数组a所占内存,sizeof(a[0])计算单个元素所占内存 
    int p[17]={0};
    int k;
    mergesort(a,0,length-1,p);
    for (int i = 0; i < length; i++)
    {
        printf("%d\t",a[i]);
        k++;
        if(k%8==0)
        	printf("\n");
    }
    printf("\n");
   	return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值