c语言-希尔排序

目录

一、插入排序

1、插入排序的概念

2、插入排序的逻辑实现

 3、插入排序的实现

二、希尔排序

1、希尔排序概念

2、希尔排序逻辑实现

3、间隔值(gap)对排序的影响

 4、希尔排序的实现

三、插入排序与希尔排序性能对比测试

结语:


前言:

        希尔排序的核心思想就是插入排序,他是在插入排序的基础上进行优化得来的,因此要想明白希尔排序的逻辑首先要认识插入排序。

一、插入排序

1、插入排序的概念

        插入排序又名直接插入排序,其思想是把要插入的数值,插入到一个有序的序列中,从而得到一个新的有序序列。现实中就跟摸牌一样,每次摸一张牌都会把这张牌插入到手中牌堆的合适位置,从而让手里的牌变得有序。

2、插入排序的逻辑实现

        排序的目的是让数组中的元素变得有序,比如冒泡排序的逻辑是从前往后遍历、相邻元素进行比较从而达到排序的功能。而插入排序的逻辑是从数组的最后一个元素往前遍历数组,举例:现如今要插入一个元素2,如果遇到大于2的元素则该元素往后“挪动”一位,直到遇到小于2的元素将2插入到该元素的后一位。

        另一种情况就是当数组内的元素都比要插入的元素要大:

 3、插入排序的实现

        在明白插入排序的逻辑后,以下用代码的形式将插入排序实现,并且测试最终结果。

#include<stdio.h>void InsertSort(int* a, int n)//插入排序
{for (int i = 0; i < n - 1; i++){int end = i;//表示当前数组的最后一个元素的下标int temp = a[i + 1];//一般而言,把end后一个元素看作是要插入进行排序的元素//因此为了防止该元素被end的元素所覆盖,因此要先保存起来while (end >= 0)//end小于0说明数组内元素都大于该插入的元素{if (a[end] > temp)//大于插入元素的情况{a[end + 1] = a[end];//“挪动”end--;//遍历end}else//小于或等于插入元素的情况break;//直接跳出}a[end + 1] = temp;//不论大于还是小于插入元素,都把要插入的元素给到end位置的后一位}
}void PrintArr(int* a, int n)//打印数组
{for (int i = 0; i < n; i++){printf("%d ", a[i]);}printf("\n");
}void Test_InsertSort()//插入排序测试函数
{int arr[] = { 66,8,6,1,2,4,3,9 };int sz = sizeof(arr) / sizeof(int);PrintArr(arr, sz);InsertSort(arr, sz);PrintArr(arr, sz);}int main()
{Test_InsertSort();return 0;
}

        其实在对数组进行插入排序时,都是在数组内进行操作的,实际上不存在“从外边来了一个元素”要插入这个数组,而是从数组的第二个元素开始,把第二个元素看成是“要插入”的元素进行插入排序的。示意图如下:

         运行结果:

二、希尔排序

1、希尔排序概念

        希尔排序是在插入排序的基础上进行优化而来的,因为插入排序如果是对一个比较有顺序的数组进行排升序,那么其时间复杂度是O(N),但是如果插入排序要把一个降序的数组排成升序,那么他的时间复杂度就等于O(N^2),而希尔排序优势就是在面对这种情况的时间复杂度依然可以做到O(N)。

        希尔排序法又称缩小增量法,他的具体步骤可以分成两步:1、预排序,就是把要排序的数组按照某个间隔值分成若干个小数组。2、然后对这若干个数组进行插入排序。其中,让间隔值不断的缩小,直到间隔值为1时,希尔排序=插入排序,只不过在这个过程中希尔排序已经让数组变得有序了,所以哪怕最后希尔排序的效率=插入排序效率,总体而言希尔排序的效率是比插入排序要高的。详细如下文。

2、希尔排序逻辑实现

        由于插入排序面对降序转升序的情况不好处理,因此介绍希尔排序的时候用一个降序的数组作为例子,现在要对一个降序的数组进行排升序,逻辑图如下:

        待到红、蓝、绿三个数组全部都排好之后,数组内顺序如下:

        可以发现此时数组里的元素并不是一个升序状态,但是比处理前更接近升序了,这时候再对其进行插入排序则可以完成对该数组的升序排序,而且时间消耗也不大。

        代码实现:

void ShellSort(int* a, int n)//希尔排序
{int gap = 3;//假设gap=3for (int j = 0; j < gap; j++)//控制小数组{for (int i = j; i < n - gap; i += gap)//对小数组进行排序{//插入排序的逻辑int end = i;int temp = a[end + gap];//注意小数组之间的间隔是gapwhile (end >= 0){if (a[end] > temp){a[end + gap] = a[end];end -= gap;}elsebreak;}a[end + gap] = temp;}}
}

        这里gap=3只能让该数组变得接近升序而不能排成升序,上文还提到了一个问题就是当gap=1时,希尔排序=插入排序,因此如果可以控制gap的值的变化,最后让gap=1就能完成对数组的升序排序了。那为什么gap=1时,希尔排序=插入排序呢,gap对排序的影响详细如下文。

3、间隔值(gap)对排序的影响

         为什么间隔值gap=1的时候希尔排序=插入排序,从上文假设当gap=3,发现小数组中的元素与元素之间相隔了2个元素,那么gap=1时,元素与元素之间相隔0个元素,此时再对这些”小数组“进行排序实则就是对整个数组进行插入排序。

        从上图可以得出,当gap=1时就是等价于插入排序的,那么gap=3、=5时会对数组里元素的顺序有什么影响吗,如下图所示:

        可以发现当gap=1时,排出来的就是一个升序数组。

        当gap=3时,排出来的结果接近升序数组。

        当gap=5时,排出来的结果反而没那么接近升序了。

结论:当gap的值越大,则越不接近升序,当gap的值越小则越接近升序。但是gap值越大有一个好处是可以把数组中数值较大的元素挪到数组的后面,较小的值放到前面。因为间隔越大,则一次移动的步长就大。

        但是gap一开始不能设置成1,不然希尔排序就没有意义了,希尔排序的优势是让gap>1的时候快速的把数组调成接近有序的,然后再用插入排序进行排序。

        因此gap的值的变化是一个从大到1的过程,可以用gap=gap/3+1来表示(gap初始值设置为该数组的元素个数),并且作为循环内部的表达式,这样一来gap的值就会不断的缩小至1,其中n表示数组的元素个数,+1的目的是保证让gap最小值为1,若不+1则会出现gap=0的情况,就会发生死循环了。

 4、希尔排序的实现

        在了解了希尔排序的逻辑以及gap的值对排序结果的影响后,用代码将其实现:

#include<stdio.h>void ShellSort(int* a, int n)//希尔排序
{int gap = n;//gap初始化为n,n表示数组内元素个数while (gap > 1){gap = gap / 3 + 1;//第一次循环先带着较大的gap去排序,后续gap的值慢慢变小,最后至1for (int j = 0; j < gap; j++)//控制小数组{for (int i = j; i < n - gap; i+=gap)//将小数组内的元素进行排序{//插入排序思想int end = i;int temp = a[end + gap];//这里小数组内的元素间隔是gap,而不是1while (end >= 0){if (a[end] > temp){a[end + gap] = a[end];end -= gap;}elsebreak;}a[end + gap] = temp;}}}
}void PrintArr(int* a, int n)//打印数组
{for (int i = 0; i < n; i++){printf("%d ", a[i]);}printf("\n");
}void Test_ShellSort()//希尔排序测试函数
{int arr[] = { 9,8,7,6,5,4,3,2,1,0 };int sz = sizeof(arr) / sizeof(int);PrintArr(arr, sz);ShellSort(arr, sz);PrintArr(arr, sz);}int main()
{Test_ShellSort();return 0;
}

        运行结果:

三、插入排序与希尔排序性能对比测试

        从代码的结构来看,会感觉到希尔排序的写法很复杂,而且逻辑也不简单,那么看起来如此复杂的希尔排序的性能一定比插入排序的性能要好吗,以下用一个测试代码进行对他们的性能测试:

#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<time.h>void ShellSort(int* a, int n)//希尔排序
{int gap = n;//gap初始化为n,n表示数组内元素个数while (gap > 1){gap = gap / 3 + 1;//第一次循环先带着较大的gap去排序,后续gap的值慢慢变小,最后至1for (int j = 0; j < gap; j++)//控制小数组{for (int i = j; i < n - gap; i += gap)//将小数组内的元素进行排序{//插入排序思想int end = i;int temp = a[end + gap];//这里小数组内的元素间隔是gap,而不是1while (end >= 0){if (a[end] > temp){a[end + gap] = a[end];end -= gap;}elsebreak;}a[end + gap] = temp;}}}
}void InsertSort(int* a, int n)//插入排序
{for (int i = 0; i < n - 1; i++){int end = i;//表示当前数组的最后一个元素的下标int temp = a[i + 1];//一般而言,把end后一个元素看作是要插入进行排序的元素//因此为了防止该元素被end的元素所覆盖,因此要先保存起来while (end >= 0)//end小于0说明数组内元素都大于该插入的元素{if (a[end] > temp)//大于插入元素的情况{a[end + 1] = a[end];//“挪动”end--;//遍历end}else//小于或等于插入元素的情况break;//直接跳出}a[end + 1] = temp;//不论大于还是小于插入元素,都把要插入的元素给到end位置的后一位}
}//对比测试
void Contrast_test()
{int n = 100000;srand(time(0));int* n1 = (int*)malloc(sizeof(int) * n);int* n2 = (int*)malloc(sizeof(int) * n);int* n3 = (int*)malloc(sizeof(int) * n);int* n4 = (int*)malloc(sizeof(int) * n);int* n5 = (int*)malloc(sizeof(int) * n);for (int i = 0; i < n; i++)//构建一个100000个元素的数组,并且存放随机值{n1[i] = rand() % 10000;n2[i] = n1[i];n3[i] = n1[i];n4[i] = n1[i];n5[i] = n1[i];}//clock函数返回的是系统启动到调用该函数的时间,单位是毫秒,并存到变量中int start1 = clock();InsertSort(n1, n);int end1 = clock();int start2 = clock();ShellSort(n2, n);int end2 = clock();printf("Test_InsertSort:%d\n", end1 - start1);//两个变量相减从而得到排序所消耗的时间printf("Test_ShellSort:%d\n", end2 - start2);free(n1);//释放空间free(n2);free(n3);free(n4);free(n5);
}int main()
{Contrast_test();return 0;
}

        运行结果:

        从结果可以观察到,插入排序的速度要比希尔排序的速度慢很多,也可以证明插入排序的消耗确实比希尔排序要大。 

结语:

        以上就是关于希尔排序的全部介绍以及实现,在掌握了希尔排序后会发现对插入排序的理解更加深刻了,其实各种不同的排序都有其存在的价值,也都有其适合的场景。最后希望本文可以给你带来更多的收获,如果本文对你起到了帮助,希望可以动动小指头帮忙点赞👍+关注😎+收藏👌!如果有遗漏或者有误的地方欢迎大家在评论区补充~!!谢谢大家!!

(~ ̄▽ ̄)~

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2021年12月 Scratch图形化(四级)真题解析#中国电子学会#全国青少年软件编程等级考试

Scratch等级考试(1~4级)全部真题・点这里 一、单选题(共15题,每题2分,共30分) 第1题 下图两个积木的值分别是? A:false true B:false false C:true true D:true false 答案:A 第2题 小猫和小狗是非常好的朋友,他们发明了一种加密方法:用两位数字代表字母。…

进程和线程的关系

⭐ 作者&#xff1a;小胡_不糊涂 &#x1f331; 作者主页&#xff1a;小胡_不糊涂的个人主页 &#x1f4c0; 收录专栏&#xff1a;JavaEE &#x1f496; 持续更文&#xff0c;关注博主少走弯路&#xff0c;谢谢大家支持 &#x1f496; 进程&线程 1. 什么是进程PCB 2. 什么是…

【设计模式-2.2】创建型——简单工厂和工厂模式

说明&#xff1a;本文介绍设计模式中&#xff0c;创建型设计模式中的工厂模式&#xff1b; 飞机大战 创建型设计模式&#xff0c;关注于对象的创建&#xff0c;本文介绍的简单工厂和工厂模式同样也是。举一个游戏例子&#xff0c;如飞机大战游戏中&#xff0c;屏幕中敌人类型…

优维低代码实践:搜索功能

优维低代码技术专栏&#xff0c;是一个全新的、技术为主的专栏&#xff0c;由优维技术委员会成员执笔&#xff0c;基于优维7年低代码技术研发及运维成果&#xff0c;主要介绍低代码相关的技术原理及架构逻辑&#xff0c;目的是给广大运维人提供一个技术交流与学习的平台。 优维…

每日一练:约瑟夫生者死者小游戏

1. 问题描述 约瑟夫问题&#xff08;Josephus problem&#xff09;是一个经典的数学和计算机科学问题&#xff0c;源于犹太历史学家弗拉维奥约瑟夫斯&#xff08;Flavius Josephus&#xff09;的著作《犹太战记》。问题的描述如下&#xff1a;   在这个问题中&#xff0c;有n…

uniapp微信小程序中阻止事件冒泡

开发场景&#xff1a;列表中展示地块的数据信息&#xff0c;用户可以点击列表进入地块的详情界面&#xff0c;也可以点击列表中的星星按钮进行收藏 遇到的问题&#xff1a;每次点击星星的时候&#xff0c;都会触发父级的点击事件&#xff0c;从而进入到详情界面 原本的代码&am…

android开发:安卓13Wifi和热点查看与设置功能

近日对安卓热点功能做了一些技术验证&#xff0c;目的是想利用手机开热点给设备做初始化&#xff0c;用的是安卓13&#xff0c;简言之&#xff1a; 热点设置功能不可用&#xff0c;不可设置SSID和密码&#xff0c;不可程序控制开启关闭&#xff0c;网上的代码统统都过时了Loca…

【数据结构】树与二叉树(廿五):树搜索给定结点的父亲(算法FindFather)

文章目录 5.3.1 树的存储结构5. 左儿子右兄弟链接结构 5.3.2 获取结点的算法1. 获取大儿子、大兄弟结点2. 搜索给定结点的父亲a. 算法FindFatherb. 算法解析c. 代码实现 3. 代码整合 5.3.1 树的存储结构 5. 左儿子右兄弟链接结构 【数据结构】树与二叉树&#xff08;十九&…

ESP32-Web-Server 实战编程-通过网页控制设备多个 GPIO

ESP32-Web-Server 实战编程-通过网页控制设备多个 GPIO 概述 上节 ESP32-Web-Server 实战编程-通过网页控制设备的 GPIO 讲述了如何通过网页控制一个 GPIO。本节实现在网页上控制多个 GPIO。 示例解析 前端设计 前端代码建立了四个 GPIO&#xff0c;如下死 GPIO 2 在前端的…

wmvcore.dll丢失怎么办?解决电脑出现wmvcore.dll丢失问题5个方法

wmvcore.dll缺失5个解决方法与wmvcore.dll丢失原因及文件介绍 引言&#xff1a; 在日常使用电脑的过程中&#xff0c;我们可能会遇到一些错误提示&#xff0c;其中之一就是wmvcore.dll缺失。wmvcore.dll是Windows Media Video编码解码相关动态链接库文件之一&#xff0c;它对…

VR全景技术助力政务服务大厅数字化,打造全新政务服务体验

引言&#xff1a; 随着科技的飞速发展&#xff0c;虚拟现实&#xff08;VR&#xff09;技术逐渐走进人们的视野。VR全景技术作为VR领域的一项重要应用&#xff0c;以其沉浸式、交互式的特点&#xff0c;正逐渐渗透到各行各业。政务服务大厅作为相关部门与民众之间的桥梁&#…

Elasticsearch(一)

一&#xff1a;简介 The Elastic Stack, 包括 Elasticsearch、 Kibana&#xff08;展示数据的项目&#xff09;、 Beats 和 Logstash&#xff08;这两个是采集和传输数据的项目&#xff09; 这些项目组合形成的技术栈称为ELK Stack&#xff0c;能够安全可靠地获取任何来源、任…

Vue框架学习笔记——键盘事件

文章目录 前文提要键盘事件&#xff08;并不是所有按键都能绑定键盘事件&#xff09;常用的按键不同的tab和四个按键keyCode绑定键盘事件&#xff08;不推荐&#xff09;Vue.config.keyCode.自定义键名 键码 神奇的猜想div标签和click.enterbutton标签和click.enter 前文提要 …

Cesium 展示——地球以及渲染数据导出(下载)为图片或 pdf

文章目录 需求分析新加需求分析第一种方式第二种方式需求 将 Cesium 球体以及渲染数据导出为 jpg/png/pdf 分析 获取场景 scene 信息,转为image 的 octet-stream 流 进行下载为图片 /*** @todo canvas 导出图片* @param {string} dataurl - 地址* @return {Blob}*/ functio…

额,收到阿里云给的赔偿了!

众所周知&#xff0c;就在刚过去不久的11月12号&#xff0c;阿里云突发了一次大规模故障&#xff0c;影响甚广。 以至于连咱们这里评论区小伙伴学校的洗衣机都崩了&#xff08;手动doge&#xff09;。 这么关键的双11节点&#xff0c;这么多热门业务和产品&#xff0c;这么大规…

平凯星辰携手教育部教育管理信息中心,助力普惠教育数字化

近日&#xff0c;企业级开源分布式数据库厂商平凯星辰与教育部教育管理信息中心达成合作&#xff0c;TiDB 分布式数据库为全国中小学管理服务平台提供全栈服务。双方将携手深入探索领先的数据库技术在教育行业的新场景与新应用&#xff0c;既夯实教育数字化底座&#xff0c;助力…

DS八大排序之直接插入排序和希尔排序

前言 我们前面几期介绍了线性和非线性的基本数据结构。例如顺序表、链表、栈和队列、二叉树等~&#xff01;本期和接下来的几期我们来详解介绍各个排序的概念、实现以及性能分析&#xff01; 本期内容 排序的概念以及其运用 常见的排序算法 直接插入排序 希尔排序 一、排序的…

Zabbix 6 详细安装部署教程

目录 一、安装 MySQL 数据库 二、安装 zabbix 监控平台 三、编辑配置文件 四、启动服务 五、zabbix-web 安装 zabbix web 出图展示乱码问题解决方案 zabbix 的安装部署非常简单&#xff0c;官方提供了四种安装途径&#xff0c;分别是二进制 rpm 包安装方式、源码安装方…

jetson nano 串口通信

目录 1.UART通信介绍 2.电脑端准备工作 2.1 安装串口调试助手 2.2 硬件接线 3.Jetson Nano端准备工作 3.1安装库文件 3.2修改主板上电启动串口权限 4.示例程序-发送及接收 4.1 开启串口调试助手 4.2 导入示例程序 4.3 执行程序 4.4 查看效果 4.4.1 串口调试端 4.4…

西南科技大学信号与系统A实验三(线性连续时间系统的分析)

一、实验目的 1.掌握用 matlab 分析系统时间响应的方法 2.掌握用 matlab 分析系统频率响应的方法 3.掌握系统零、极点分布与系统稳定性关系 二、实验原理 1. 系统函数 H(s) 系统函数:系统零状态响应的拉氏变换与激励的拉氏变换之比. H(s)=R(s)/E(s) 在 matlab 中可采用…