目录

一.归并排序介绍

二.归并排序算法的特点

三.归并算法的实现介绍 


一.归并排序介绍

      归并排序是建立在归并操作上的一种有效,稳定的排序算法,该算法是采用分治法的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列。

二.归并排序算法的特点

        排序算法有很多,插入,冒泡,选择,希尔,快速等等,我们通过上面的这张图会发现,归并算法的时间复杂度是非常稳定的,不论是在哪种情况下,归并算法的时间复杂度都不变,而且,归并算法计算效率相比其他算法也是非常快的。

        上面我们提到了归并算法的优点,世无完物,拥有较快的效率和极高的稳定性的归并算法,想必也有自己的缺点,没错,归并算法的缺点就是他的空间复杂度比较大,而产生这样的缺点就是因为他的分治思想导致的。

三.归并算法的实现介绍 

 上面的这张图就是归并算法的大体排序过程,可以看到归并算法就是要把一个有n个元素的数组,分成n个有1个元素的数组。然后再进行排序的。而具体的过程,我将以上图中5 ,7, 1, 8,这四个元素的排列来介绍。

 上面的这个就是我们在进行把两个有序数组合并成一个有序数组的简单图解,下面是以代码的形式来书写这个过程。

void merge_sort(int* arr, int left, int right, int middle)
{
	int* aux;
	int i = left;//i表示的是middle前面的数组的元素
	int j = middle+1;//j表示的是middle之后的数组的元素
	int k = 0;//k表示的是aux数组中的元素
	aux = (int*)malloc((right-left+1)*sizeof(int));//为了开辟够足够的空间开存放数组各元素的内容
	for (k= left; k <= right; k++)
	{
		*(aux + k - left) = *(arr + k);//先将两个子数组中的元素全部输入进aux数组中
	}
	for (k = left; k <= right; k++)
	{
		if (i > middle)
		{
			*(arr + k) = *(aux + j - left);
			j++;
		}
		else if (j > right)
		{
			*(arr + k) = *(aux + i - left);
			i++;
		}
		else if (*(aux + i - left) > *(aux + j - left))
		{
			*(arr + k) = *(aux + j - left);
			j++;
		}
		else
		{
			*(arr + k) = *(aux + i - left);
			i++;
		}
	}
}

       这个就是具体的合并过程。

      在前面的八个元素的大体排序图中,我们发现归并排序的分治法的治是前面分的倒序过程,通过我们之前学习的知识,不难想到,这个归并的过程其实是一个递归的过程具体代码如下:

void mergesort(int* arr, int left, int right)
{
	if (left >= right)
	{
		return ;
	}
	int middle = (left + right) / 2;
	mergesort(arr, left, middle);
	mergesort(arr, middle + 1, right);
	merge_sort(arr, left, right, middle);
}

除此之外,我们还需要编写一个函数,用来确定数组的首元素的编号和最后一个元素的编号。

void Mergesort(int* arr, int left, int right)
{
	mergesort(arr, left, right - 1);
}

接下来再书写用来接收输入的数据的main函数

int main()
{
	int* arr;
	int n = 0;
	int i = 0;
	scanf("%d", &n);
	arr = (int*)malloc(n * sizeof(int));
	for (i = 0; i < n; i++)
	{
		scanf("%d", arr + i);
	}
	Mergesort(arr, 0, n);
	for (i = 0; i < n; i++)
	{
		printf("%d ", *(arr + i));
	}
	return 0;
}

下面就是由四个函数拼接而成的归并排序

#include <stdlib.h>
#include <stdio.h>
void Mergesort(int* arr, int left, int right);
void mergesort(int* arr, int left, int right);
void merge_sort(int* arr, int left, int right, int middle);

int main()
{
	int* arr;
	int n = 0;
	int i = 0;
	scanf("%d", &n);
	arr = (int*)malloc(n * sizeof(int));
	for (i = 0; i < n; i++)
	{
		scanf("%d", arr + i);
	}
	Mergesort(arr, 0, n);
	for (i = 0; i < n; i++)
	{
		printf("%d ", *(arr + i));
	}
	return 0;
}

void Mergesort(int* arr, int left, int right)
{
	mergesort(arr, left, right - 1);
}

void mergesort(int* arr, int left, int right)
{
	if (left >= right)
	{
		return ;
	}
	int middle = (left + right) / 2;
	mergesort(arr, left, middle);
	mergesort(arr, middle + 1, right);
	merge_sort(arr, left, right, middle);
}

void merge_sort(int* arr, int left, int right, int middle)
{
	int* aux;
	int i = left;//i表示的是middle前面的数组的元素
	int j = middle+1;//j表示的是middle之后的数组的元素
	int k = 0;//k表示的是aux数组中的元素
	aux = (int*)malloc((right-left+1)*sizeof(int));//为了开辟够足够的空间开存放数组各元素的内容
	for (k= left; k <= right; k++)
	{
		*(aux + k - left) = *(arr + k);//先将两个子数组中的元素全部输入进aux数组中
	}
	for (k = left; k <= right; k++)
	{
		if (i > middle)
		{
			*(arr + k) = *(aux + j - left);
			j++;
		}
		else if (j > right)
		{
			*(arr + k) = *(aux + i - left);
			i++;
		}
		else if (*(aux + i - left) > *(aux + j - left))
		{
			*(arr + k) = *(aux + j - left);
			j++;
		}
		else
		{
			*(arr + k) = *(aux + i - left);
			i++;
		}
	}
}

这么长的代码再加上递归,别说是用肉眼看,就是通过调试,也会不小心的把人绕进递归递归,所以这边博主以简单的三元素数组{3,2,1}来解释这个代码:

        这里的一个难点是递归的过程,程序员需要时刻清楚递归的环节,在{3,2,1}的第一次递归后,将这个数组分成了{3,2}和{1},通过代码,我们知道先把前面的数组拆分,即{3,2}会被拆分为{3},{2},这时前面的数组就算是完成了拆分,而{2}的数组本身就只有一个元素,所以不需要进行{2}数组的递归,进入后面的{3},{2}的排序,在{3,2}变成{2,3}后,我们终于跳出了前面数组两层递归的里面一层,再次跳入后面{1}数组的递归,{1}数组本身就只有一个元素,所以不需要进行{1}数组的递归,进入{2,3}和{1}的排序,排序完成后,我们也跳出了全部的递归,完成了排序。

这就是我对归并排序的讲解,如有问题,欢迎你的斧正。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐