排序(4)——归并排序

目录

前言

1.归并排序的递归实现

1.1 归并排序概念

1.2 归并排序递归实现

2.归并排序的非递归实现


前言

今天给大家带来比较排序的最后一种,归并排序,这个排序,需要我们对递归,循环控制要有着较强的理解,我相信大家通过前面和小编的一起学习,这里肯定也是得心应手。


1.归并排序的递归实现

1.1 归并排序概念

归并排序( MERGE-SORT )是建立在归并操作上的一种有效的排序算法 , 该算法是采用分治法( Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。 归并排序核心步骤:
排序(4)——归并排序_第1张图片

1.2 归并排序递归实现

这里我们看具体过程:

排序(4)——归并排序_第2张图片

这里有个细节我们需要注意一下,在我们对数组进行排序的时候我们需要利用另外一个数组,然后在复制回原数组。

 这里我们的具体代码如下:

void _MergeSort(int* a, int*temp,int begin, int end)
{
	if (begin >= end)
	{
		return;
	}
	int mid = (begin + end) / 2;
	_MergeSort(a, temp, begin, mid);
	_MergeSort(a, temp, mid + 1, end);
	int begin1 = begin, end1 = mid;
	int begin2 = mid + 1, end2 = end;
	int i = begin;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (a[begin1] >=a[begin2])
		{
			temp[i++] = a[begin2];
			begin2++;
		}
		else
		{
			temp[i++] = a[begin1];
			begin1++;
		}
	}
	while (begin1 <= end1)
	{
		temp[i++] = a[begin1];
		begin1++;
	}
	while (begin2 <= end2)
	{
		temp[i++] = a[begin2];
		begin2++;
	}
	memcpy(a + begin, temp + begin, sizeof(int)*(end - begin + 1));
}

//归并排序
void MergeSort(int* a,int n)
{
	int* temp = (int*)malloc(sizeof(int) * n);
	if (temp == NULL)
	{
		perror("malloc fail");
		return;
	}
	_MergeSort(a, temp, 0, n - 1);
	free(temp);
	temp = NULL;
}

这里由于我们需要使用到另外的数组去进行存储排序,但是由于局部变量在每次函数调用结束后会自动销毁,所以这里我们需要把数组建立在堆区,但是我们创建数组的操作是不能进行递归的,如果递归就会不断出现重复的数组,因此我们这里只需要把排序和复制回元组的操作进行递归,因此这里我们封装函数MergeSort以及函数_MergeSort.

由于这里我们是将数组元素不断分解直到剩一个元素,或者没有元素,然后先进行左右分解区间的排序,最后返回上一级,直到所有的元素都排序完毕,我们就直接结束排序。

这里我给大家详细讲解一下代码:

排序(4)——归并排序_第3张图片

 

2.归并排序的非递归实现

对于非递归实现,之前小编给大家讲解了快速排序的非递归实现,但是相对于今天的归并排序,前者和树的前序遍历类似,后者和树的后序遍历类似,对于此类的递归我们,一般是从最小空间到最大空间不断控制,但是这个过程中有很多的细节需要我们自己掌握一下。

这里小编给大家演示一下具体过程:

排序(4)——归并排序_第4张图片

 这里我们发现gap=gap*2,只有当我们的数组长度等于2的倍数的时候我们才能刚好分组,否则就会出现某些问题,例如:

排序(4)——归并排序_第5张图片

那么对于这里的越界我们一共可能会出现三种越界情况,我们要对其分别进行控制

具体情况如下:

1.end1越界了,不归并

2.end1没有越界,begin2越界了,也不归并

3.end1没有越界,begin也没越界。end2越界了,我们需要修正end2

这里我们该怎么理解呢?首先我们需要知道一点,我们的分组是在小组已经排好序的情况下分的,所以我们要明白,begin1~end1这段区间是有序的,begin2~end2这段区间是有序的,但是begin1~end2这段不一定有序(如果都存在的话),那么对于情况一,如果end1越界说明这两段区间都不存在,所以我们就不需要将其归并,对于情况二,如果begin2越界了,但是end1没越界,由于begin1~end1,已经有序,所以我们也就不需要归并,对于情况三,如果只是begin2~end2部分越界,那么我们只需要将begin1~end1和begin2到数组末尾出现的元素进行归并即可。

那么我们的代码如下:

void MergeSortNonR2(int* a, int n)
{
	int* temp = (int*)malloc(sizeof(int) * n);
	if (temp == NULL)
	{
		perror("malloc fail");
		return;
	}
	int gap = 1;
	int i = 0;
	while (gap < n)
	{
		int j = 0;
		for (i = 0; i < n; i = i + 2 * gap)
		{
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + gap * 2 - 1;
			if (end1 >n-1&&begin2 > n - 1)
			{
				break;
			}
			else if(end2>n-1)
			{
				end2 = n - 1;
			}
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] <= a[begin2])
				{
					temp[j++] = a[begin1++];
				}
				else
				{
					temp[j++] = a[begin2++];
				}
			}
			while (begin1 <= end1)
			{
				temp[j++] = a[begin1++];
			}
			while (begin2 <= end2)
			{
				temp[j++] = a[begin2++];
			}
			memcpy(a+i, temp+i, sizeof(int)*(end2-i+1));
		}
		gap = gap * 2;
	}

}

 代码详解如下:

排序(4)——归并排序_第6张图片

你可能感兴趣的:(排序算法,算法,数据结构)