C语言实现插入排序和希尔排序(动态图演示过程)

news2024/12/28 20:18:17

插入和希尔

  • 插入排序
    • 时间和空间复杂度分析
  • 希尔排序
    • 时间和空间复杂度分析

本篇文章将插入排序和希尔排序放在一起讲解,是因为后者可以说是前者的排序方式的一种优化,思路上大体一样,插入和希尔在整个排序的大章节中,算是比较简单的,与冒泡排序一样,相信大家理解起来也不会那么的困难

插入排序

在排序这一章节,很有意思的是,各种排序可以根据它们的名字猜出大概的思路,就比如插入排序,冒泡排序,快速排序等,从名字就可以看出各种排序的特点来。

插入排序的思路(文字)
我们先以一组比较短的数据来讲解一下插入排序的总体思路。

以数组【7,6,5,2,3,1】为例
我们将第二个数字 6 标记为end 将数组的第一个数字到end之间的所有数字看作成一个小的数组,即【7, 6】。 将end (6) 与end-1 (7) 进行比较,大小的向后移动,移动完之后,就是【6, 7】。 然后将end向后移动一位,到达数字 5 。此时数组中的数据为【6,7,5,2,3,1】

然后将【6,7,5】看作一个小数组进行排序,首先end (5) 和end-1 (7) 进行比较,大的向后移动,数组变成【6,5,7】 然后end向前移动一位,到达5的位置,然后end (5) 和 end-1 (6) 进行比较,大的数向后移动,数组变成【5,6,7】

此时数组【5,6,7,2,3,1】
然后end再从原来的位置向后移动一位,到达2的位置,然后重复的进行上述的比较,直到end走到数组的最后一个数子。

上述就是插入排序思路的文字解释了,当热对初学者来说是晦涩难懂,不过没关系,下面就用动图给大家演示一下这个过程,之后大家再结合下面的代码看一遍,保证药到病除!!

动图演示
画图的不好,勿喷
在这里插入图片描述
其实每次移动的就只有end而已,因为end-1会随着end的变化而变化的,其次就是,每次比较end和end-1,只要end-1比end大就交换两这,也就是上述所说到的大的数字向后走。

从其中我们不难看出来,这个过程是需要有两个循环的,一层循环控制的是end,当end走到数组外排序就完成了,内层循环是控制end和end-1之间小数组的,内层循环结束的条件就是end>0,因为当end是第一个数据的时候,end-1就会出现错误❌。

看到这里我想大家可以分析一下时间复杂度了,待会我们再来看时间复杂度的分析。

下面我们直接给出代码:

#include <stdio.h>
void Insert(int* a, int n)
{
  int i = 0;
  for(i = 1; i < n; ++i)
  {
    int end = i;
    while(end > 0)
    {
        if(a[end] < a[end-1])
        {
        //交换
          int tmp = a[end];
          a[end] = a[end-1];
          a[end-1] = tmp;
        }
        end--;
    }
  }
}

void Test()
{
  int arr[] = {-3,1,2,3,5,7,19,2,15,10,8,3,7,-1,-2};
  int sz = sizeof(arr) / sizeof(arr[0]);

  Insert(arr, sz);
	
  //打印数据
  int i = 0;
  for(i = 0; i < sz; i++)
  {
    printf("%d ", arr[i]);
  }
  printf("\n");
}

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

时间和空间复杂度分析

实现了算法之后我们就来谈一下时间复杂度和空间复杂度吧

首先很好理解的是空间复杂度肯定是O(1),不理解的去补一下时间复杂度和空间复杂度的计算吧。

再然后就是时间复杂度的计算,其实也很好理解,每一次end无论是在小数组中遍历还是在大数组中进行遍历,都是n,所以整体的时间复杂度就是 O(n^2)。

希尔排序

上面我们说过希尔排序是对插入排序的一种优化,插入排序虽然做到了成功排序,但是在时间复杂度上还是消耗太大,接下来我们看一下希尔排序是怎么对插入排序进行优化的。

希尔排序的思路(文字)
为了更好的讲解希尔排序的思路,我们以一个比较长一点的数组为例
以数组【-3,1,2,3,5,7,19,2,15,10】为例

希尔排序与插入排序的区别在于,希尔排序会对原数据进行一个预排序
OK,下面先来讲解一下什么是预排序:
插入排序的时候,是直接以数组的第二个数字为end,依次往后的进行排序,每次和end进行比较的都是end-1,这就导致了时间复杂度是N^2的结果,但是如果我们在判断小数组的数据是否有序时,有序就跳出来,那么再假设此时的数据大部分都是有序的话,有一些的小数组就不必进如循环,从而帮我们节省了一定的时间。

现在我们要做的就是如何让原本错乱的数据出现一定的有序数字,这就是我们的预排序需要做的事情了,此时我们就需要跳跃式的进行插入排序,所谓的跳跃式就是,不再直接先对相邻的两个数字进行判断,而是先对相距一定距离的两个数字进行大小的判断,再通过不短的缩短数字之间的间距,从而对数据进行一个预排序的目的。

我们将这个间距暂时的命名为gap。

当gap从大到小不断缩短的这个过程就是预排序的过程,当gap为1的时候,就是插入排序了,只不过在gap为1的时候,由于预排序的作用,数据大概都已经是有序的了。

此时再进行一次插入排序,数据就会完全的有序。

看到这里的话,如果你是之前了解过希尔排序的,相信已经能写出代码来了。加油老铁!

同样的接下来我们还是用动态图来演示一遍上述的过程。

动图演示

勿喷!老铁。
在这里插入图片描述
这里我只演示了gap第一次值为4的走法,这趟循环过后,我们就可以再次通过gap = gap / 3 + 1来缩小gap,再次让数据更加的趋向有序一点。

所以我们是需要有三层循环的,第一层来控制gap的缩小
第二层我们来控制end的结束,我们不难发现,end结束的条件就是小于 n - gap
第三层来控制以gap为间距的小数组的排序

现在我们来解释为什么是gap = gap / 3 + 1,其实gap的起始值为多少都是可以的,只不过不能是太小(要不然预排序就发挥不特性了),关键的是,我们需要让gap的最后一次的值为1,因为只有gap的最后一次的值为1,才能完成对预排序过后数据的完全有序,gap / 3 + 1最终的值就只能为1,此时就正好进行最后一次的插入排序。

下面是代码:

void Shell(int* a, int n)
{
    int gap = n;

    while (gap > 1)
    {
        gap = gap / 3 + 1; 

        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;
                }
            }
            //来的这里有两种可能
            //1.前面的数据都已经有序了
            //2.小的数据向前走
            a[end + gap] = tmp;
        }
    }
}

Test1()
{
    int arr[] = { -3,1,2,3,5,7,19,2,15,10,8,3,-7,1,2,-10 };
    int sz = sizeof(arr) / sizeof(arr[0]);

    Shell(arr, sz);

    int i = 0;
    for (i = 0; i < sz; i++)
    {
        printf("%d ", arr[i]);
    }
    printf("\n");
}
int main()
{
    Test1();
    return 0;
}

刚开始看不懂的话,大家可以以上面的数据为例,自己用笔画着走一遍,大致也就能明白了。

时间和空间复杂度分析

关于希尔排序的空间复杂度肯定也是O(1)了,希尔排序的时间复杂度就比较的难推了,我只认为自己写的不够好,就截断了,有人觉得,这个也每比直接插入排序快多少吧,但是它还是要比直接插入排序要快的,时间复杂度大概为O(n^(2/3)),证明大家感兴趣的话,可以找一下相关的文章看一下,但是没必要,你只要知道它比直接插入排序要快一些就OK了。

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

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

相关文章

java食堂库存管理系统源码

简介 Java基于sprinboot开发的食堂库存管理系统&#xff0c;用于统计食堂库存的&#xff0c;包含采购、入库、出库、折损等功能。 演示视频 https://www.bilibili.com/video/BV1Jf4y1C7vq/?share_sourcecopy_web&vd_sourceed0f04fbb713154db5cc611225d92156 调试 https…

php宝塔搭建部署实战响应式儿童益智玩具网站模板源码

大家好啊&#xff0c;我是测评君&#xff0c;欢迎来到web测评。 本期给大家带来一套php开发的响应式儿童益智玩具网站模板源码&#xff0c;感兴趣的朋友可以自行下载学习。 技术架构 PHP7.2 nginx mysql5.7 JS CSS HTMLcnetos7以上 宝塔面板 文字搭建教程 下载源码&am…

java基于ssm空气质量检测系统源码网站空气质量监测源码

简介 Java基于ssm的空气质量检测系统&#xff0c;检测设备检测一定范围内的企业空气指数&#xff0c;如果有污染则地图显示红色标记。 演示视频 https://www.bilibili.com/video/BV1GK4y1W7JB/?share_sourcecopy_web&vd_sourceed0f04fbb713154db5cc611225d92156 技术 …

67、NeRF-Editing: Geometry Editing of Neural Radiance Fields

简介 允许用户对场景的隐表示进行可控的形状变形&#xff0c;在不重新训练网络的情况下合成编辑过的场景的新视图图像。在提取的显式网格表示(Mesh)和目标场景的隐式神经表示之间建立了对应关系&#xff0c;利用基于网格的变形方法&#xff08;ARAP&#xff09;对场景的网格表…

CAN 协议及标准规格

文章目录CAN协议对应ISO/OSI 基本参照模型ISO 标准化的 CAN 协议通信速度和最大总线长度的关系根据协议不同选择对应IC其他标准规格CAN协议对应ISO/OSI 基本参照模型 CAN 协议中关于 ISO/OSI 基本参照模型中的传输层、数据链路层及物理层 数据链路层分为 MAC 子层和 LLC 子层…

Vue3基础知识,看这篇文章就够啦~

0 前言整理了一下自己在学Vue3的时候的笔记&#xff0c;如果有错误的地方还望指正~1 setup函数1.1 参数 setup(props,context)props&#xff1a;父组件传递过来的属性context&#xff1a;SetupContext&#xff0c;即是setup函数的上下文1.1.1 参数1 props如果想在setup函数中使…

脑电项目探索和实现(EEG) (上):研究数据集选取和介绍SEED

数据集介绍&#xff1a; 使用上海交大的SEED数据集1 SEED数据集包含12名受试者的脑电图和眼动数据以及另外3名受试者的脑电图数据。数据是在他们观看电影片段时收集的。电影片段是精心挑选的&#xff0c;以诱导不同类型的情绪&#xff0c;积极的&#xff0c;消极的&#xff0…

BI技巧丨子类Top及其他

BOSS&#xff1a;白茶&#xff0c;能不能在展示产品销量的时候&#xff0c;前三的展示&#xff0c;其他的都归为“其他”啊&#xff1f; 白茶&#xff1a;可以啊&#xff01;安排&#xff01; 在实际项目中&#xff0c;一张Dashboard看板的设计&#xff0c;既要考虑逻辑的准确无…

Spring中@Conditional注解详解

文章目录Conditional是Spring4新提供的注解&#xff0c;它的作用是按照一定的条件进行判断&#xff0c;满足条件给容器注册bean。首先创建Dog类然后创建MyCondition类定义两个Bean测试方法AnnotationConfigApplicationContext测试ConditionalOnBean和ConditionalOnMissingBean注…

51单片机学习笔记_8 IICAT24C02 芯片的应用

I2C EEPROM I2C I2C&#xff08;Inter&#xff0d;Integrated Circuit&#xff09;总线是由 PHILIPS 公司开发的两线式 串行总线&#xff0c;用于连接微控制器及其外围设备。 I2C 结构 I2C 只有两根双向信号线&#xff0c;一根是 SDA 数据线&#xff0c;一根是 SCL 时钟线。…

2-选择题练手

1.HASH函数冲突处理方式不包括以下哪一项 A.开放定址法 B.链地址法 C.插入排序法 D.公共溢出区发 答&#xff1a;C 析&#xff1a; HASH函数冲突处理方式有&#xff1a; 开放定址法&#xff1a;&#xff08;线性探测再散列&#xff0c;二次探测再散列&#xff0c;伪随机…

Netty进阶——粘包与半包(固定分隔符方式解决粘包问题)

目录一、固定分隔符方式解决粘包问题&#xff08;代码示例&#xff09;1.1、固定分隔符解决粘包问题的服务端代码示例1.2、固定分隔符方式解决粘包问题的客户端代码示例1.3、分别启动服务端&#xff0c;客户端&#xff0c;查看服务端结果输出一、固定分隔符方式解决粘包问题&am…

vitepress(四):引入vue组件

这节课的内容需要有前置的良好的Vue基础&#xff0c;如果你仅仅想搭建一个存放md文件的网站的话&#xff0c;可以不必学习后面的内容&#xff0c;当然如果你想个性化自己的站点&#xff0c;那么推荐你学习一下引用的方式和注意点&#xff0c;开始你的个性化之旅 编写VUE组件 …

ARES Map地理信息系统(GIS)

ARES Map地理信息系统(GIS) ARES地图是GRAEBERT的地理信息系统(GIS)产品。该软件是一个复合解决方案&#xff0c;它将GIS的知识和内容放在一个CAD丰富的DWG系统上&#xff0c;并允许您同时使用它们。该软件的地图和设计将以DWG格式自然存储&#xff0c;但它们也可以包含GIS信息…

【阶段三】Python机器学习02篇:机器学习项目流程

本篇的思维导图: 机器学习项目流程 大致分为以下6个环节: (1)项目背景(问题定义) (2)数据收集 (3) 数据预处理与探索性数据分析 (4) 特征工程 (5)构建模型:机器学习模型(算法)的选择

TCP 慢启动突发丢包

TCP 慢启动会导致持续突发丢包。 慢启动以 y2xy2^xy2x 增加窗口&#xff0c;在 BDP 已经填满时&#xff0c;后续的慢启动过程如下&#xff1a; ​每一个 ACK 触发 2 个 报文&#xff0c;最终至少丢掉 1 个 BDP 的数据后 sender 才能检测到丢包而退出慢启动并进行重传。 这是…

C语言蓝桥杯刷题:明码

题目链接 本题为填空题&#xff0c;只需要算出结果后&#xff0c;在代码中使用输出语句将所填结果输出即可。 汉字的字形存在于字库中&#xff0c;即便在今天&#xff0c;16*16 点阵的字库也仍然使用广泛。 16*16 点阵的字库把每个汉字看成是 1616 个像素信息。并把这些信息记…

电子书销售是一种可以躺赚的商业模式么?

文章目录前言电子书销售市场规模到底有多大&#xff1f;电子书产业链电子书阅读平台电子书销售平台国外国内其它销售模式探讨创建电子书创建电子书的工具电子书下载好去处相关法规前言 不知何时&#xff0c;有了网赚一词&#xff0c;例如去各大平台撸羊毛薅羊毛&#xff0c;利…

Vector - VT System - 继电器板卡_VT2820

这块板卡应该是我个人最喜欢的板卡了&#xff0c;虽然说有很多的模拟板卡、数字板卡等仿真板卡&#xff0c;但是在实际的应用中&#xff0c;我们仿真还是很难做到绝对的信号一致&#xff0c;但是如果有实际的硬件&#xff0c;通过继电器板卡就很好的模拟实际车上的场景&#xf…

定时器(Timer)

一、定时器是什么&#xff1f; 定时器类似于我们生活中的闹钟&#xff0c;可以设定一个时间来提醒我们。 而定时器是指定一个时间去执行一个任务&#xff0c;让程序去代替人工准时操作。 标准库中的定时器: Timer 方法作用void schedule(TimerTask task, long delay)指定dela…