排序-----归并排序(递归,非递归)

原理:归并排序就是利用递归和分治,将数据序列划分为越来越小的半子表,再对半子表排序,最后再用递归将排序好的半子表合并成越来越大的有序序列。

具体而言,就是对于给定的n个数据,首先将每相邻的长度为1的子序列进行归并,得到n/2个长度为1或2的有序子序列,再将其两两归并,直到得到一个有序序列为止。

例如:对于数组{49,38,65,97,76,13,27}为例,排序过程如下:

第一步:[49][38]   [65][97]   [76][13]   [27] 

第二步:[38,49]    [65,97]    [13,76]    [27]

第三步:[38,49,65,97]     [13,27,76]

第四步:[13,27,38,49,65,76,97]

二路归并排序的过程需要进行logn次,每次归并的时间复杂度为O(n),所以总的时间复杂度为O(nlogn)

递归:

//归并排序
#include<stdio.h>

//归并
void Merge(int a[], int p, int q, int r)
{
	int i, j, k, n1, n2;
	n1 = q - p + 1;
	n2 = r - q;
//给整型指针L,R分配内存空间
	int* L = new int[n1];
	int* R = new int[n2];
//将数组的值分别放入L和R中
	for (i = 0, k = p;i < n1;++i, ++k)
		L[i] = a[k];
	for (i = 0, k = q + 1;i < n2;++i, ++k)
		R[i] = a[k];
//两数组从大到小比较,直到一个数组遍历完为止
	for (k = p, i = 0, j = 0;i < n1&&j < n2;++k)
	{
		if (L[i] < R[j])
		{
			a[k] = L[i];
			++i;
		}
		else
		{
			a[k] = R[j];
			++j;
		}
	}
//如果L还没排序完,则把剩下的数依次放在a[k]后
	if (i < n1)
	{
		for (;i < n1;++i, ++k)
			a[k] = L[i];
	}
//如果R还没排序完,则把剩下的数依次放在a[k]后
	if (j < n2)
	{
		for (;j < n2;++j, ++k)
			a[k] = R[j];
	}
}

//归并排序
void MergeSort(int a[], int p, int r)
{
//当p<r时执行,直到两数组个数均为1时
	if (p < r)
	{
		int q = (p + r) / 2;
		MergeSort(a, p, q);
		MergeSort(a, q + 1, r);
		Merge(a, p, q, r);
	}
}
int main()
{
	int array[] = { 1,4,7,9,2,3,5,8 };
	int len = sizeof(array) / sizeof(array[0]);
	MergeSort(array, 0, len - 1);
	for (int i = 0;i < len;++i)
		printf("%d ", array[i]);
	printf("\n");
	return 0;
}

非递归:

#define LENGTH 10
#include <stdio.h>
#include <stdlib.h>

//归并
void Merge(int *S, int *T, int low, int mid, int high)
{
	int j, k, l;
	for (k = low,j = mid+1;low<=mid&&j<=high;++k)
	{
		if (S[low] < S[j])
			T[k] = S[low++];
		else
			T[k] = S[j++];
	}
	if (low <= mid)
	{
		for (l = 0;l <= mid - low;++l)
			T[k + l] = S[low+l];
	}
	if (j <= high)
	{
		for (l = 0;l <= high - j;l++)
			T[k + l] = S[j + l];
	}
}

void MergePass(int *S, int *T, int w, int n)
{
	int s =w,l;
	int i = 0;
	while ((i + 2 * s - 1) < LENGTH)
	{
		Merge(S, T, i, i + s - 1, i + 2 * s - 1);
		i += 2 * s;
	}
//把i到i+s和i+s到LENRTH-1再归并
	if (i + s - 1 < LENGTH)
	{
		Merge(S, T, i, i + s - 1, LENGTH - 1);
	}
//如果不满足i+s-1<LENGTH则表示剩下的为有序,直接赋给数组即可
	else
	{
		for (l = i;l < LENGTH;++l)
			T[l] = S[l];
	}
}

void MergeSort(int *a)
{
	int *temp = (int*)malloc(sizeof(int)*LENGTH);
//k用来表示每次k个元素归并
	int k = 1;
	while (k < LENGTH)
	{
//先借助辅助空间归并
		MergePass(a, temp, k, LENGTH);
//k每次乘以2是遵循1->2->4->8->16的二路归并元素个数的原则
		k *= 2;
//再从辅助空间将排过序的序列归并回原数组
		MergePass(temp, a, k, LENGTH);
		k *= 2;
	}
}

int main()
{
	int i;
	int array[LENGTH] = { 1,3,5,7,9,2,4,6,8,10 };
	
	printf("Before Sorting:\n");
	for (i = 0;i < LENGTH;++i)
		printf("%d ", array[i]);
	printf("\n");

	MergeSort(array);
	printf("After Sorting:\n");
	for (i = 0;i < LENGTH;++i)
		printf("%d ", array[i]);
	printf("\n");
	return 0;
}

非递归的方法,避免了递归时深度为log2(n)的栈空间,空间只是申请归并临时用到的temp数组,因此空间复杂度O(n),并且避免递归也在时间性能上有一定的提升。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值