【手撕插入排序和希尔排序】

这篇具有很好参考价值的文章主要介绍了【手撕插入排序和希尔排序】。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


插入排序概念

直接插入排序是从一个有序的序列中选择一个合适的位置进行插入,这个合适的位置取决于是要升序排序还是降序排序。

每一次进行排序之后,这段数据都是有序的。


提示:以下是本篇文章正文内容,下面案例可供参考

插入排序分为2种

一 .直接插入排序

直接插入排序是从一段数据中将一个数据在合适的位置插入。

案例:
一张图弄懂直接插入排序
【手撕插入排序和希尔排序】
【手撕插入排序和希尔排序】

代码如下:

void InsertSort(int * a,int n )
{
	for(int i =0;i<n-1;i++)
	{
		int end = i;
		//保存待插入元素
		int tmp = a[end+1];
		while(end>=0)
		{
			if(a[end]>tmp)
			{
				//把end往后挪
				a[end+1] = a[end];
				//end再往前走	
				end--;
			}
			else
			{
				break;
			}
		}
		//由于不管是在中间的任意地方插入还是在end的末尾插入(即tmp是最大的情况),
		//都是在end后面的位置插入,所以来到这里进行合并
		a[end+1] = tmp;
	}
}

直接插入排序时间复杂度

直接插入排序的时间复杂度为:O(N^2),因为最坏的情况是逆序的情况:
【手撕插入排序和希尔排序】

每一次插入需要挪动的次数为:1+2+3+4+…+n-2+n-1 = n*n/2

所以最坏情况下的时间复杂度为O(n^2)

二.希尔排序

希尔排序可以被认为是优化后的直接插入排序。

具体优化过程如下:

给定一个gap,这个gap是把待插入的数据分成gap组,每组之间的间隔为gap长度
给定一个gap,这个gap是把待插入的数据分成gap组,每组之间的间隔为gap长度
给定一个gap,这个gap是把待插入的数据分成gap组,每组之间的间隔为gap长度

重要的事情说三遍。

比如:

【手撕插入排序和希尔排序】
令gap = 3,即待插入的数据的间隔为3,不同于直接插入排序,直接插入排序是第一个和第二个数据的间隔永远为1,而对于希尔排序,当gap = 3时,第一个数据和第二个数据的间隔为3。
当我们把该组的元素两两比较时,大的元素就会更快地往后走。

【手撕插入排序和希尔排序】
第二轮是将待插入元素8和9比较,因为9后面的第一个元素不再是7,而是9+gap的位置处的数据,即8
再将9和8进行比较,将8插入到9位置处。

当然,这是每组组内的比较,

放眼整个希尔排序来说,是多组同时进行的

可以发现,
当gap越大,大的元素越快挪到后面
当gap越小,小的元素越慢挪到后面

当gap == 1时,就相当于上面提到的直接插入排序。

回到上面的案例,gap = 3,所以需要将数据分成3组,每组的间隔为3个长度。

【手撕插入排序和希尔排序】

如上图:此时每个元素都可以被覆盖到。

【手撕插入排序和希尔排序】

相当于同时把gap组中大的元素更快挪到后面

我们把上面的过程成为:预排序

也就是说,完成上面的操作之后,整个数据并不是有序的,而是 接近有序

比如上面的案例,完成预排序后,整组数据为:

【手撕插入排序和希尔排序】
此时是接近有序,所以此时令gap = 1,即最后接近有序的时候进行直接插入排序即可。

注意:gap的取值是不确定的:
gap取值越大,大的数据越快挪到后面,但越不接近有序
gap取值越小,大的数据越慢挪到后面,但越接近有序

总之gap是一定不能固定,并且gap的取值最后必须为1。

gap的取值应该是从大逐渐到小过渡的。

gap的取值一般是:

初始化gap = n,

进入轮回时:

gap = gap/3+1 或者 gap = gap/2,每次轮完一轮后,gap都会减小。

当gap的取值是gap = gap/2时,时间复杂度为:O(N*logN),logN是以2为底N的对数

最坏情况同样为逆序:

【手撕插入排序和希尔排序】
最后一轮gap = 1,此时为直接插入排序,则N/2/2/2/…/2 = 1,
每次轮回一次gap,gap都会/2,最后一次gap = 1,则需要比较的次数是logN(以2为底N的对数)

希尔排序时间复杂度

总的时间复杂度为遍历整组元素的次数:O(N)*每次遍历进行插入的次数O(logN)
—> O(N * logN)

同理:当gap的变化是gap = gap/3-1时, 最坏情况下(逆序)每次轮回需要插入的次数是

(((N/3+1) /3+1)/3+1)… = 1
对于时间复杂度:可忽略掉+1项,所以每次轮回插入次数log3 (N) ,以3为底N的对数

总时间复杂度为O(N*log3 (N))

经过前人计算,希尔排序平均时间复杂度为:
O(N^1.3)
【手撕插入排序和希尔排序】
实现代码:

void ShellSort(int* a, int n)
{
	//当gap越大,大的值越快到达最后的位置,但越不接近有序
	//当gap越小,大的值越慢到达最后的位置,但越接近有序

	//当gap值越接近1,排序越接近顺序
	//刚gap == 1时,就是直接插入排序
	int gap = n;

	while (gap > 1)
	{
		//两种方式均可,gap可以任取任何值,但是都必须保证gap最后一定为1
		//gap = gap / 2;
		gap = gap / 3 + 1;

		//在这里就是把间隔多组的数据同时排列
		for (int i = 0; i < n - gap; i++)
		{

			int end = i;
			int tmp = a[end + gap];

			while (end >= 0)
			{

				//小于的情况,需要挪动数据
				if (a[end] > tmp)
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				//大于或者等于的情况,直接插入end后面
				else
				{
					break;
				}
			}
			//由于最终都需要插入end后面,所以在循环之外插入
			a[end + gap] = tmp;
		}

	}
}

总结:希尔排序是在直接插入排序的基础上引入一个gap,这个gap把数据分成了gap组,并且每组元素之间的间隔也为gap。
gap每次都会逐渐减小,并且最后gap一定为1,当gap为1时,代表完成了预排序,
最后一步进行直接插入排序。


效率比较

void TestOP()
{
	srand(time(0));
	const int N = 1000000;
	int* a1 = (int*)malloc(sizeof(int) * N);
	int* a2 = (int*)malloc(sizeof(int) * N);
	assert(a1 && a2);
	for (int i = 0; i < N; ++i)
	{
		a1[i] = rand();
		a2[i] = a1[i];
	}

	//计算到这个走位置的时间(ms)
	int begin1 = clock();
	InsertSort(a1, N);
	int end1 = clock();
	//末位置-初位置就是时间差

	int begin2 = clock();
	ShellSort(a2, N);
	int end2 = clock();
	
	printf("InsertSort:%d\n", end1 - begin1);
	printf("ShellSort:%d\n", end2 - begin2);

	free(a1);
	free(a2);
}

int main()
{
	TestOP();
	return 0;
}

【手撕插入排序和希尔排序】
可以看到,当排序数据为100w个时,直接插入排序和希尔排序差距超过了2000倍。

直接插入排序和希尔排序的效率相比,数据越多,希尔排序较于直接插入排序的优化越大。文章来源地址https://www.toymoban.com/news/detail-404001.html

到了这里,关于【手撕插入排序和希尔排序】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 排序算法:插入排序(直接插入排序、希尔排序)

    朋友们、伙计们,我们又见面了,本期来给大家解读一下有关排序算法的相关知识点,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成! C 语 言 专 栏: C语言:从入门到精通 数据结构专栏: 数据结构 个  人  主  页 : stackY、 目录   前言: 1.排序

    2024年02月09日
    浏览(50)
  • 插入排序(一)——直接插入排序与希尔排序

    目录 一.前言 二.排序的概念及其运用 1.1排序的概念 1.2 常用排序算法 三.常用排序算法的实现 3.1 插入排序 3.1.1 基本思想 3.1.2 直接插入排序 3.1.3 希尔排序(缩小增量排序) 四.全部代码 sort.c sort.h test.c 五.结语 本文我们开始进入数据结构的难点——排序,当我们初步学习排序

    2024年01月20日
    浏览(34)
  • 排序:直接插入排序&希尔排序

    目录 排序: 概念: 直接插入排序:  代码的实现:  代码解析:  总结: 希尔排序:  代码实现:  预排序:  代码优化:  gap 的 本质  : 直接插入排序:  代码图解: 总结:  所谓排序,就是使一串记录,按照其中的某个或某些的大小,递增或递减的排列起来

    2024年02月04日
    浏览(33)
  • 直接插入排序与希尔排序

    目录 前言 插入排序  直接插入排序  时空复杂度 直接插入排序的特性 希尔排序(缩小增量排序) 预排序 顺序排序 多组并排 小总结 直接插入排序 时空复杂度 希尔排序的特性 字可能有点多,但是真的理解起来真的没那么难😘 记得一定要连起来看,我把排序的实现过程拆

    2024年02月04日
    浏览(35)
  • 【数据结构与算法篇】手撕排序算法之插入排序与希尔排序

    ​👻内容专栏:《数据结构与算法篇》 🐨本文概括: 讲述排序的概念、直接插入排序、希尔排序、插入排序和希尔排序的区别。 🐼本文作者:花 碟 🐸发布时间:2023.6.13 排序 :所谓排序,就是使一串记录,按照其中的某个或某些的大小,递增或递减的排列起来的

    2024年02月09日
    浏览(52)
  • 【数据结构】排序之插入排序(直接插入排序||希尔排序)

    在生活中处处可见排序,当我们打开京东或者其它购物平台时,搜索物品,它会有一定的排序。 这次就来分享的博客与排序有关。 正文开始。 排序 :所谓排序,就是使一串记录,按照其中的某个或某些的大小,递增或递减的排列起来的操作。 稳定性 :假定在待排序

    2024年01月16日
    浏览(48)
  • 排序第一课【插入排序】直接插入排序 与 希尔排序

    目录 1. 排序的概念: 2.插入排序基本思想 3.直接插入排序 4.希尔排序 排序: 所谓排序,就是使一串记录,按照其中的某个或某些的大小,递增或递减的排列起来的操作。 稳定性: 假定在待排序的记录序列中, 存在多个具有相同的的记录,若经过排序,这些记

    2024年02月14日
    浏览(65)
  • 【排序算法 上】带你手撕常见排序 (插入,希尔,选择,堆排序) (动图详解)

    欢迎来到 Claffic 的博客  💞💞💞 “东风随春归,发我枝上花。” 前言:  排序是日常生活中极其常见的一种算法,它的功能很简单,就是将数字按照升序/降序排列,最终形成一组有序的数字,不过形成有序数字的过程有多种实现方式,它们各有好坏,接下来,由我带你手

    2023年04月19日
    浏览(46)
  • 【排序算法】排序算法介绍及插入排序 ( 直接插入排序 && 希尔排序 )

    ​ ​📝个人主页:@Sherry的成长之路 🏠学习社区:Sherry的成长之路(个人社区) 📖专栏链接:数据结构 🎯 长路漫漫浩浩,万事皆有期待 排序 :所谓排序,就是将一串数据,按照某种规律,或者以某种特性或,将数据按照递增或者递减,将数据从 无序转变为有序

    2023年04月21日
    浏览(42)
  • 十大排序算法(上)直接插入排序、希尔排序、直接选择排序、堆排序

    排序,就是使一串记录,按照其中的某个或某些的大小,递增或递减的排列起来的操作。 稳定性 :假设在待排序的序列中,存在多个具有相同内容的元素,如果经过排序,这些元素的相对位置并不发生改变,则称这种排序算法是稳定的。 稳定的排序可以变成不稳定的

    2024年02月05日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包