算法学习日记4---进阶排序1--归并排序

       OK啊,这边也是放寒假了,继续学习!

       排序一直是很基础但与此同时也很深奥的话题,通过以前的学习,我们接触到了时间复杂度为O(n²)的排序算法:冒泡排序,选择排序,插入排序,每一种排序都有各自的优点,比如冒泡排序,它的代码简单容易记,上手快;再比如插入排序,受数组本身有序性的影响较大,如果数组有序性较强,插入排序就是不二之选;再比如选择排序,它胜在稳定。那么大家有没有感觉到这些排序算法为什么虽然能完成排序的要求,但其实并没有那么优秀呢?是因为上述基本排序都浪费掉了大量的比较过程,也就是说比较后的信息没有办法保存下来,导致很多数据比较了很多次,最后才会比较出结果。那么有没有一些排序算法不会浪费比较过程呢?归并排序就是其中一种。

       归并排序

       我们为什么要学习归并排序?因为归并排序快,很多OJ都会有时间复杂度和额外空间复杂度的要求,这时候用快的排序算法才不会超时!

       现在先来介绍一下归并排序的基本算法。先随便给出一个乱序的数组,对它进行升序排序。

       简单来说,归并排序就是首先把一个数组分为左边和右边两个部分,让这两个部分各自有序,然后这两个部分再次将本身划分成左右两个部分,再让这两个部分各自有序,以此类推知道各自各自元素只有一个时,开始从后往前合并。就以上特征可以看出,这是递归的本质,同时也是的本质,也就是说上述过程需要递归实现。需要注意的是,上述过程完成递归分为两个部分,也就是左部分和右部分,只有当同一层递归的过程中结果都出现的时候,这层递归才能执行下去,如上图红色箭头的顺序,是从后往前的!

       讲完了归并排序的大致思路,我们再来看具体一步是怎么执行的!这里我就拿最后一步左右部分的数组合并过程举例子。

       上图中,p1用来指向左侧数组中的元素,p2用来指向右侧数组中的元素,红色箭头表示p1和p2指针的移动,红色箭头上的数字表示指针移动的次序。每一次讲讲p1和p2所指向的数值进行比较,将较小的数拷贝到help数组的i位置,然后i后移1位,以此类推知道两个数组都越界,help数组就拷贝完成,且该数组有序,最后将help数组里的值拷贝回arr数组里面去,输出arr就是排好序的数组啦1!

       至此归并排序的基本思路已经讲完,上代码!

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
void displayarr(int* arr, int size)
{
	printf("the array is:");
	for (int i = 0; i < size; i++)
	{
		printf("%d ",arr[i]);
	}
	printf("\n");
}
void merge(int* arr, int size, int first, int mid, int last)
{
	int* help = (int*)malloc(size * sizeof(int));
	int i = 0;
	int p1 = 0;
	int p2 = mid + 1;
	while (p1 <= mid && p2 <= last)
	{
		help[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
		//哪个指针所指向的数值较小,就拷贝哪一个给help,然后对应完成自增操作
		//注意:help[i++]是先对help[i]进行操作,再对i进行自增
		//如果是help[++i],则是先对i进行自增,再对help[i]进行操作
	}
	while (p1 <= mid)
	{
		help[i++] = arr[p1++];
	}
	while (p2 <= last)
	{
		help[i++] = arr[p2++];
	}
	for (int j = 0; j < i; j++)
	{
		arr[j] = help[j];//将辅助空间的值拷贝给原数组
	}
	free(help);
	//每一个动态分配的内存都需要最后free掉,就是malloc和calloc最后都要free
}
//size传进去的作用是为了动态分配内存申请空间
void mergesort(int* arr, int size,int first,int last)
{
	if (first >= last)
	{
		return;
		//递归可以理解成栈,就是先入后出的原理,如果没有return,也就是不返回的话,就不会实现递归的操作,因此对于递归来说return十分重要
	}
	//求出中间值
	int mid = first + (last - first) / 2;
	//对以中间值为划分的区域各自mergesort,使得左右部分都有序
	mergesort(arr, size, first, mid);
	mergesort(arr, size, mid + 1, last);
	merge(arr, size, first, mid, last);
}
int main()
{
	int arr[] = { 6,5,7,8,2,3 };
	int size = sizeof(arr) / sizeof(int);
	printf("原始数组:");
	displayarr(arr, size);
	mergesort(arr, size, 0, size - 1);
	printf("归并排序数组:");
	displayarr(arr, size);
	return 0;
}

       值得注意的是,归并排序的算法时间复杂度是O(n*logn),额外空间复杂度是O(n),额外空间复杂度很好理解,就是利用了一个help数组,help数组的个数和本身乱序的数组的元素个数相等,而时间复杂度为何是O(n*logn),这个涉及到master公式,这里就不做过多解释了,只需要知道确实是比一般排序要快即可。

       至此我对归并排序的理解就讲完了,如有不妥,欢迎指出,感谢您看到这里!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值