希尔排序-C语言版本

news2024/11/24 4:49:58

前言

从希尔开始,排序的速度就开始上升了,这里的排序开始上一个难度了,当然难一点的排序其实也不是很难,当你对于插入排序了解的足够深入的时候,你会发现其实希尔就是插入的异形,但是本质上还是一样的

希尔排序gif

希尔排序单趟实现

1,希尔排序本质就是插入排序的进化,插入排序是寻找比较进行插入,希尔这个人觉得插入排序有点慢,就觉得我们可不可以进行预排序,也就是数值原来是0-9的情况下,最坏的情况我们需要循环9次数才能找到需要插入的点在哪,那么此时我们能不能进行一个预排序

2,所谓的预排序就是,我们假设几个为一组,几个为一组,这个组的变量我们设置为gap。假设处置3个为一组,那么gap=3

3,此时我们可以把这一组,先采取插入排序的方式进行预排序,预排序之后我们就会发现这一组的这条线上的数值已经有序

4,多趟实现只是反复的实现第一趟的实现的逻辑

希尔排序的多趟实现

1,多趟实现只是反复的实现第一趟的实现的逻辑

2,我们需要先知道单趟遍历的时候,我们需要假设gap一组的,这个中间的元素是没有的,那么此时此时一组就是两个数值,我们需要让这两个数值进行交换

3,这两个数值交换之后我们这个时候需要循环开始插入排序的逻辑,也就是假设前面的两个数值是已经有序的,那么新插入的一个数值是需要排序的,我们进行排序

4,一趟结束之后,我们继续多趟实现,这几个数值继续预排序

5,继续预排序

6,此时gap=3,那么我们继续,gap/2=1,之后继续进行预排序,此时我们就得到了这个排序正确的数值

希尔排序代码的实现-版本1

//希尔排序
void ShellSort(int* a, int n)
{
	int gap = n - 1;
	//定义gap,这里循环停止的条件是>1,原因是+1了,已经恒大于0了,所以需要大于1,等于都不可以
	while (gap > 1)
	{
		gap = gap / 3 + 1;
		//循环实现每一组
		for (int j = 0; j < gap; j++)
		{
			//gap单趟实现
			for (int i = j; i < n - gap; i += gap)
			{
				int end = i; int tmp = a[end + gap];
				//一组的实现
				while (end >= 0)
				{
					if (tmp < a[end])
					{
						a[end + gap] = a[end];
						end -= gap;
					}
					else
					{
						break;
					}
				}
				//交换找到的数值
				a[end + gap] = tmp;
			}
		}
	}
}

 解释:

  1. 函数ShellSort接受两个参数:一个指向整数数组a的指针和数组的长度n

  2. 初始化间隔gap为数组长度n - 1,这是希尔排序开始时的最大间隔。

  3. while循环用于逐步减小间隔,直到间隔为1。当gap大于1时,循环继续。

  4. 在每次while循环的开始,重新计算间隔gap。这里使用的是gap = gap / 3 + 1,这是一种常见的间隔序列,由Donald Shell提出。

  5. 外层for循环用于遍历数组,步长为当前的间隔gap

  6. 内层for循环用于实现插入排序,但仅限于间隔gap内的范围。

  7. 在内层循环中,end变量记录当前考虑的元素的索引,tmp变量暂存当前要插入的元素。

  8. while循环用于在间隔gap内从后向前扫描,找到tmp应该插入的位置。

  9. 如果tmp小于当前比较的元素a[end],则将a[end]向后移动一个间隔gap的距离,为tmp腾出空间。

  10. 如果tmp大于或等于a[end],则while循环结束,找到了tmp应该插入的位置。

  11. 循环结束后,将tmp赋值给a[end + gap],完成插入操作。

  12. 这个过程重复进行,直到数组中的所有元素都被扫描并插入到正确的位置。

代码逻辑:

  • 希尔排序通过多个插入排序的变体来工作,每个变体都有一个特定的间隔。
  • 初始时,间隔较大,排序的稳定性较差,但可以快速减少无序度。
  • 随着间隔逐渐减小,排序的稳定性逐渐提高,最终当间隔为1时,算法变为普通的插入排序,确保数组完全有序。

注意:

  • 希尔排序不是稳定的排序算法,因为它可能会改变相等元素的相对顺序。
  • 希尔排序的时间复杂度通常在 𝑂(𝑛log⁡𝑛) 和 𝑂(𝑛^2) 之间,具体取决于间隔序列的选择。
  • 希尔排序的空间复杂度是 𝑂(1,因为它是原地排序算法,不需要额外的存储空间。

希尔排序代码的实现-版本2

上面那个代码一般是教学使用,书写会更加详细理解,但是很多书本会这样写

这里的关键在于,不再进行先一组排序结束,再反过来进行下一组反复执行

而是直接这一组实现完毕之后,++,直接进行下一组的实现,本质上还是一样的

只是直接这样书写,容易不理解

//希尔排序
void ShellSort02(int* a, int n)
{
	int gap = n - 1;
	//定义gap,这里循环停止的条件是>1,原因是+1了,已经恒大于0了,所以需要大于1,等于都不可以
	while (gap > 1)
	{
		gap = gap / 3 + 1;
		//循环实现每一组+//gap单趟实现
		for (int i = 0; i < n - gap; i++)
		{
			int end = i; int tmp = a[end + gap];
			//一组的实现
			while (end >= 0)
			{
				if (tmp < a[end])
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			//交换找到的数值
			a[end + gap] = tmp;
		}
	}
}

解释:

  1. 函数ShellSort02接受两个参数:一个指向整数数组a的指针和数组的长度n

  2. 初始化间隔gap为数组的最后一个索引n - 1

  3. while循环用于逐步减小间隔,直到间隔小于或等于1。循环的条件是gap > 1,这是因为间隔在每次迭代中都会减小,所以不需要检查gap == 1的情况。

  4. while循环内部,重新计算间隔gap。这里使用的计算方法是gap = gap / 3 + 1,这是一种常见的间隔序列,旨在逐步减小间隔,直到达到1。

  5. 外层for循环遍历数组,直到i < n - gap,即遍历到数组的末尾减去当前间隔的位置。

  6. 在外层for循环中,end变量记录当前考虑的元素的索引,tmp变量暂存当前间隔位置的元素值。

  7. 内层while循环用于在数组中找到tmp应该插入的位置。它从当前索引end开始,向前以间隔gap的距离进行比较。

  8. 如果tmp小于当前比较的元素a[end],则将a[end]向后移动一个间隔gap的距离,为tmp腾出空间。

  9. 如果tmp大于或等于a[end],则while循环结束,找到了tmp应该插入的位置。

  10. 循环结束后,将tmp赋值给a[end + gap],完成插入操作。

  11. 这个过程重复进行,直到数组中的所有元素都被扫描并插入到正确的位置。

代码逻辑:

  • 希尔排序通过多个插入排序的变体来工作,每个变体都有一个特定的间隔。
  • 初始时,间隔较大,随着算法的进行,间隔逐渐减小,直到变为1,此时算法变为普通的插入排序。
  • 通过逐步减小间隔,希尔排序能够在每次迭代中对数组的更大范围内的元素进行排序,从而减少比较和移动的次数。

希尔排序gap的界定

一般来说,一组为多少这个不好说,希尔开始书写的时候,gap是/2,来进行书写的,但是被认为效率最高的是,除以3+1,但是/3+1有一个弊端就是这个是恒大于0的,所以终止条件应该换为大于1就继续循环,小于等于1就停止循环

希尔排序的时间复杂度

希尔排序的时间复杂度取决于所选择的间隔序列,它通常介于以下范围:

  1. 最好情况:对于某些特定的间隔序列,希尔排序可以达到O(nlogn) 的时间复杂度,这与快速排序或归并排序相当。

  2. 平均情况:平均情况下,希尔排序的时间复杂度通常被认为是 O(n(logn)2),这比=O(nlogn) 稍差,但仍然比普通插入排序的 𝑂(𝑛^2) 好得多。

  3. 最坏情况:最坏情况下,希尔排序的时间复杂度可以退化到𝑂(𝑛^2),这通常发生在间隔序列选择不佳时。

  4. 实际性能:在实际应用中,希尔排序的性能通常比普通插入排序好,但不如快速排序、堆排序或归并排序。它的实际性能还取决于数据的初始状态和间隔序列的选择。

  5. 间隔序列的影响:不同的间隔序列对希尔排序的性能有很大影响。例如,使用Hibbard 间隔序列或Sedgewick间隔序列可以提高性能,有时甚至可以达到接近 O(nlogn) 的效率。

  6. 空间复杂度:希尔排序是原地排序算法,其空间复杂度为 𝑂(1)。

  7. 稳定性:希尔排序不是稳定的排序算法,这意味着相等的元素在排序后可能会改变它们原来的顺序。

总的来说,希尔排序是一种有效的排序算法,特别是对于中等大小的数据集或者当数据已经部分有序时。然而,对于大型数据集,通常推荐使用其他更高效的排序算法,如快速排序、堆排序或归并排序。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1834574.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Android可穿戴设备世界之旅

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 介绍 Android通过在电视、穿戴和汽车等各种电子模块中扩展下一代应用开发概念&#xff0c;扩展了其整个范围和可…

在整合spring boot+layui中解决Could not parse as expression: “的问题

首先查看报错信息&#xff0c;这里提示我们78行有问题 这里是[[]] 这个内联表达式出了问题&#xff0c;在当前所在的script标签中加入th:inlinenone&#xff0c;然后重启项目&#xff0c;成功解决&#xff01;

碳课堂 | 手把手教你申报CBAM

CBAM全称为 Carbon Border Adjustment Mechanism&#xff0c;也被称作“碳关税”或“碳边境调节机制”&#xff0c;是指在实施国内严格气候政策的基础上&#xff0c;要求进口或出口的高碳产品缴纳或退还相应的税费或碳配额。目前&#xff0c;由于欧盟碳边境调节机制是全球第一个…

Javaweb之web开发概述

一、Javaweb简介 用Java技术来解决相关web互联网领域的技术栈.使用JAVAEE技术体系开发企业级互联网项目. 项目规模和架构模式与JAVASE阶段有着很大的差别. 在互联网项目下,首先需要明白客户端和服务器的概念 客户端 :与用户进行交互&#xff0c;用于接收用户的输入(操作)、展示…

S32K3通过S32DS实现:S32K3如何将FLASH驱动放到RAM里面、RAM如何实现软件复位数据不丢失操作。

目录 1、概述 2、默认flash存放位置展示 3、通过默认的链接文件将flash放置到RAM 4、通过修改启动与链接文件将flash放在RAM 5、RAM热复位数据不丢失 1、概述 在通过RTD的SDK也好MCAL也好,始终存在一个问题,生成的代码除了看门狗模块,默认都是放在flash里面,按照正常逻…

团队管理的三个要点,打造高执行力团队

一、明确目标与责任 明确的目标与责任是团队高效运作的基石。只有当团队成员对目标有清晰的认识&#xff0c;并明确自己的责任时&#xff0c;才能形成强大的合力&#xff0c;推动团队不断前进。 1、目标设定 目标应该具体、可衡量、有挑战性但可实现。项目经理可以与团队成员…

拐点 万维钢电子书(拐点万维钢下载在线阅读)

本文节选自《拐点万维钢》在线阅读 医院急诊室有个特别常见的状况是病人胸口痛。对这种情 况&#xff0c;医生必须判断是不是心脏病&#xff0c;是心脏病就得赶紧处置。但问题 是&#xff0c;急诊医生并没有很好的诊断方法。 通常的做法是搞个正式的检查&#xff0c;而心脏病检…

Rust 实战丨HTTPie

概述 之前学习过《陈天Rust 编程第一课 - 04&#xff5c;get hands dirty&#xff1a;来写个实用的 CLI 小工具》&#xff0c;学的时候迷迷糊糊。后来在系统学习完 Rust 后&#xff0c;重新回过头来看这个实战小案例&#xff0c;基本上都能掌握&#xff0c;并且有了一些新的理…

【C语言】解决C语言报错:Uninitialized Variable

文章目录 简介什么是Uninitialized VariableUninitialized Variable的常见原因如何检测和调试Uninitialized Variable解决Uninitialized Variable的最佳实践详细实例解析示例1&#xff1a;局部变量未初始化示例2&#xff1a;数组未初始化示例3&#xff1a;指针未初始化示例4&am…

Transformer革新:Infini-Transformer在长文本处理中的突破

在当今信息爆炸的时代&#xff0c;大型语言模型&#xff08;LLMs&#xff09;在处理长文本数据方面的需求日益增长。无论是科学研究、法律分析还是医学诊断&#xff0c;长文本的处理能力都显得尤为重要。然而&#xff0c;现有的基于Transformer的模型在处理这类数据时遇到了重大…

keepalived服务详解与实验 基于centos8

目录 keepalivedHA简介常用的高可用软件keepalived简介 keepalived常用模块keepalived功能简介keepalived常用文件keepalived配置文件详解keepalived实验1-上手环境准备安装服务主配置文件修改启动服务效果查看 keepalived脑裂1. 脑裂现象简介2. 脑裂的原因3. 脑裂的预防和解决…

【需求管理】软件需求开发和管理文档(原件Word)

1. 目的 2. 适用范围 3. 参考文件 4. 术语和缩写 5. 需求获取的方式 5.1. 与用户交谈向用户提问题 5.1.1. 访谈重点注意事项 5.1.2. 访谈指南 5.2. 参观用户的工作流程 5.3. 向用户群体发调查问卷 5.4. 已有软件系统调研 5.5. 资料收集 5.6. 原型系统调研 5.6.1. …

【数据结构】第十七弹---C语言实现选择排序

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】 目录 1、选择排序 1.1、基本思想 1.2、代码实现 1.3、代码测试 1.4、时空复杂度分析 总结 1、选择排序 1.1、基本思想 选择排序是一种简单直观的比…

safari浏览器无法连接到服务器

问题&#xff1a;MacBook pro&#xff0c;网络连接正常&#xff0c;可以使用各种软件上网&#xff0c;唯独safari浏览器打不开网页&#xff0c;报错说Safari无法连接到服务器&#xff1b; 原因&#xff1a;使用了VPN&#xff0c;VPN自动更改了网络设置&#xff0c;导致Safari浏…

PythonPoc基础编写(3)---批量刷cnvd

文章目录 前言一、发现过程二、使用步骤1.引入库2.读入数据结果 总结 前言 想刷cnvd&#xff1f;最重要的是登录进行测试功能点 一、发现过程 找到一个网站 发现登录失败返回200 登录成功则是重定向 302 那就写一个脚本吧 二、使用步骤 1.引入库 import requests 2.读入…

【前端项目笔记】2 主页布局

主页布局 element-ui提供的组件名称就是它的类名 ☆☆ CSS选择器&#xff1a; &#xff08;1&#xff09;基本选择器 类型选择器 p/span/div…… 类选择器 (.classname) ID选择器 (#idname) 通配选择器 ( * ) &#xff08;2&#xff09;属性选择器 选择具有特定属性或属性值的…

掌控Linux-Conda环境安装终极指南

Linux-Conda环境安装教程 一、引言1.1. conda的作用与优势优势&#xff1a; 1.2. 简述conda在Linux系统中的重要性重要性&#xff1a; 二、准备工作2.1. 系统要求与兼容性Linux发行版支持情况硬件资源需求 2.2. 安装前的必要工具wget或curl的安装必要的开发库 三、下载与安装Mi…

计算机相关专业是否仍是“万金油”的选择?

亲爱的朋友们&#xff1a; 2024 年高考已然落幕&#xff0c;数百万高三学子站在了人生的重要十字路口&#xff0c;面临着选择大学专业这一关键抉择。在这个节点上&#xff0c;计算机相关专业是否还能被称为“万金油”的选择呢&#xff1f; 相信大家都知道&#xff0c;在最近这几…

Apache Doris 之 Docker 部署篇

前言 在现代数据驱动的商业环境中&#xff0c;实时数据分析和高并发查询能力是企业成功的关键因素之一。传统的数据仓库和分析工具在面对大规模数据处理和实时分析需求时&#xff0c;往往力不从心。Apache Doris 作为一个现代的 MPP 数据库管理系统&#xff0c;凭借其强大的查…

预埋螺栓抗滑移系数检测 内六角螺栓扭矩系数检测

螺栓检测范围&#xff1a;螺栓&#xff0c;高强螺栓&#xff0c;地脚螺栓&#xff0c;不锈钢螺栓&#xff0c;六角头螺栓&#xff0c;管片螺栓&#xff0c;膨胀螺栓&#xff0c;化学螺栓&#xff0c;镀锌螺栓&#xff0c;植筋螺栓&#xff0c;普通螺栓&#xff0c;钢结构螺栓&a…