首页 > 代码库 > 算法:归并算法的递归与非递归形式

算法:归并算法的递归与非递归形式

    归并算法是将两个或两个以上的有序表组合成一个新的有序表,它的原理是:假设初始序列含有n个记录,则可以看成是n个有序子序列,两两归并,得到[n/2]个有序子序列,再次归并……不断重复直至归并到长度为n的有序序列,这样的排序方法称为2路归并排序。


实例一:递归形式的2路归并算法

#define MAXSIZE 4

int data[MAXSIZE] = {2,1,0,3};

/*
*	功能:将from数组min到max-1下标数据排好序,最后的结果是to[min]...to[max-1]
*	输入:1.待排序的数组;2.排好序的数组;3.相关位置设定
*	输出:无
*/
void merge(int from[],int to[],int min,int m,int max)
{
	/*将from[]数组两侧元素排序放在to[]上,直至一侧"溢出"*/
	int i = min , j = m+1;  
	while(min<=m && j<=max)  
	{
		if(from[min] < from[j])
			to[i++] = from[min++];
		else
			to[i++] = from[j++];
	} 	
	/*由于子序列已经排好序,当上面的循环结束时,将还没"溢出"的一侧剩余元素直接赋给后面的to[]*/
	if(min <= m)  
		for(int k =0; k<=m-min; k++)
			to[i+k] = from[min+k];
	if(j <= max)
		for(int k = 0; k <= max-j; k++)
			to[i+k] = from[j+k];
}

/*
*	功能:不断分组归并
*	输入:1.待排序的数组;2.排好序的数组;3.相关位置设定
*	输出:无
*/
void mSort(int from[],int to[],int min,int max)
{
	if(min == max)  // 不断分组,直至拷贝了一份原数组
		to[min] = from[max];
	else
	{
		int m = (min+max)/2;  
		int temp[MAXSIZE];         // 创建一个新的数组来存储排序的子序列
		mSort(from,temp,min,m);    // 此时中间下标变为最大下标 , temp数组递归后为下一层的要megre的to数组
		mSort(from,temp,m+1,max);  // 此时中间下标+1变为最小下标
		merge(temp,to,min,m,max);  // 分到不能再分就开始不断归并  
	}
}

void print(int *data)
{
	for(int i = 0; i < MAXSIZE; i++)
		printf("%d ",data[i]);
	printf("\n");
}

void main(int argc, char* argv[])
{
	print(data);  
	mSort(data,data,0,MAXSIZE-1);
	print(data);
}
打印结果:


由程序可以看出,每分组一次就要创建一个临时存放数据的数组,这无疑会降低性能,所以可以采用非递归形式的归并算法。


实例二:非递归形式的2路归并算法

#include "stdio.h" 
#include "malloc.h"

#define MAXSIZE 8

int data[MAXSIZE] = {5,2,1,7,6,4,0,3};


/*
*	功能:归并排序,自下而上,不采取递归方式,而是通过算法自下而上
*	输入:待排序数组,数组元素个数
*	输出:无
*/
void merge_sort(int *list, int length)
{
	int i, left_min, left_max, right_min, right_max, next;
	int *tmp = (int*)malloc(sizeof(int) * length);  // 由此至终都是和这个动态分配的数组打交道
	

	for (i = 1; i < length; i *= 2)  // 当i = 1时代表要归并的序列大小为1,依次类推
	{
        /*当前大小的序列需要归并的次数,通过改变left_min来移动*/
		for (left_min = 0; left_min < length - i; left_min = right_max)  
		{
			right_min = left_max = left_min + i;  // 序列切分为两半后相关位置设定
            right_max = right_min + i;
			
			if (right_max > length)
				right_max = length;
			
			next = 0;

			/*将两组序列排序进入tmp直至一段"溢出",尤其注意right_min的移动*/
			while (left_min < left_max && right_min < right_max)
				tmp[next++] = list[left_min] > list[right_min] ? list[right_min++] : list[left_min++];
			
			/*如果溢出的是右端,待排序的数组读取左端后面较大的数据*/
            while (left_min < left_max)
				list[--right_min] = list[--left_max];
			
			/*将之前存放到tmp的数组的数据继续读到数组*/
            while (next > 0)
                list[--right_min] = tmp[--next];
		}
	}
    free(tmp);
}


void main()
{
	print(data);
	merge_sort(data,MAXSIZE);
	print(data);
}

打印结果基本同上


    归并排序很重要的一个思想是,在归并的子序列是已经排好序的,这也是其中算法的关键。归并排序的时间复杂度是O(nlogn),处理大量数据时性能远比简单排序算法要好,其中非递归归并算法的空间复杂度比递归归并算法的要小,整体性能较好。


算法:归并算法的递归与非递归形式