排序算法---直接插入排序和希尔排序

…衆ロ難τιáo~ 提交于 2019-12-04 19:07:45

排序算法大致可分为:

1.插入排序 --- 直接插入排序、希尔排序

2.选择排序 --- 选择排序、堆排序

3.交换排序 --- 冒泡排序、快速排序

4.归并排序

本篇博客主要介绍插入排序。。

ps:以下两种排序算法都是以升序为例讲解!


一、直接插入排序

直接插入排序的思想:

假设数组第一个数是有序的,从第二个数开始遍历,每拿出一个数就和前面的有序的数比较,如果比它小,就往前面插;否则就排在后边,插入后将前面的有序数组+1。这样走下去,前面的一部分数组总是有序的,直到遍历完这个数组后,整个数组也是有序的。(其实也就是相当于把数组分为有序和无序的两部分)


代码解决:

我们可以设置一个pos,使pos来遍历前一个有序的数组,当从无序数组中拿出一个数据a[i]时,用pos找到这个数的位置,再进行插入

void InsertSort(int* arr,size_t size)
{
	assert(arr);
	//假设第一个是有序的
	for (size_t index=1; index<size; ++index)
	{
		int pos = index-1;
		int tmp = arr[index];
		while (pos >= 0 && arr[pos] > tmp)
		{
			arr[pos+1] = arr[pos];
			pos--;
		}
		arr[pos+1] = tmp;
	}
}

直接插入排序的复杂度:

从空间复杂度上看,这种排序算法只需要一个辅助空间来存放临时记录。

从时间复杂度上看,最好的情况是这组数据近似有序或其本身就是有序的,比如上边图示的一组数是{0,2,4,3,5,6},排序时只是多移动了一次,即只需要遍历N+1次。就算数据比较多时,这组数据也是近似有序,那么移动的次数也就只有常数次,因此这种情况下的实践复杂度是O(N)最坏的情况是数组完全逆序时,假设这组数据时{6,5,4,3,2,0},那么就需要比较n+(n-1)+(n-2)+......(可以理解为是一个等差数列求和),因此这种情况下的时间复杂度为O(N^2)



二、希尔排序

希尔排序的思想:

希尔排序其实是在插入排序的基础上做了一些优化,即将数组先分组,然后对各组进行排序,即预排序;当进行多次预排序以后数组近似有序,再进行直接插入排序。

这样分组的目的是:当数组完全逆序或近似这种情况时,希尔排序会将大的数据近况向后面移动,小的数据会尽快向前面移动,其实也就是解决了直接插入排序的最坏情况


代码:

void ShellSort(int* arr,size_t size)
{
	assert(arr);
	
	//gap是每次分组的相隔的距离,gap越大,预排序后的数据越不近似有序;
	//gap越小,挪动的次数太多,因此这里的关键是选取一个合适的gap
	//int gap = 3;O
	int gap = size;
	while (gap > 1)
	{
		gap = gap/3 + 1;	//保证gap最后会为1
		//预排序
		for (int index=gap; index<size; ++index)
		{
			int pos = index-gap;
			int tmp = arr[index];	//保存当前index位置的数据
			while (pos>=0 && arr[pos]>tmp)	//升序
			{
				arr[pos+gap] = arr[pos];
				pos -= gap;
			}
			arr[pos+gap] = tmp;
		}
	}
}

希尔排序的复杂度:

O(N^1.25 ~ 1.6N^1.25)之间,不会超过O(N^2).


希尔排序比直接进插入排序算法好在哪里?

希尔排序本身就是在插入排序的基础上做的优化了,可能当数据比较少的时候,希尔排序的优势并不明显。但当数据量大的时候,并且这些数据完全逆序或近似逆序时(也就是插入排序最坏的情况),希尔排序就会优于插入排序,因为它的几趟预排序就使这些数据接近有序了。


易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!