【排序算法】插入排序_直接插入排序、希尔排序

文章目录

  • 直接插入排序
    • 直接插入排序的基本思想
    • 直接插入排序的过程
    • 插入排序算法的C代码
    • 举例分析
    • 插入排序的复杂度分析
    • 插入排序的优点
  • 希尔排序
    • 希尔排序(Shell Sort)详解
    • 希尔排序的步骤:
    • 希尔排序的过程示例:
    • 希尔排序的C语言实现
    • 举例分析:
    • 希尔排序的复杂度分析:
    • 希尔排序的优点和缺点

从本篇文章开始,博主将持续更新排序算法的内容,排序算法如下:

直接插入排序

直接插入排序(Simple Insertion Sort)是一种简单的排序算法,基于插入的思想进行排序。它的工作原理类似于我们整理扑克牌的过程:一开始我们手里没有任何牌,每次从桌上取一张牌,将其插入到手中已排好序的牌中,直到手中所有的牌都排好序。

直接插入排序的基本思想

  • 将待排序的数组分为两部分:已排序部分未排序部分
  • 初始时,已排序部分只有一个元素(通常是数组的第一个元素),未排序部分包含数组中剩下的元素。
  • 每次从未排序部分取出一个元素,插入到已排序部分的合适位置,使已排序部分仍然是有序的。
  • 重复这一过程,直到未排序部分为空。

直接插入排序的过程

  1. 从数组的第二个元素开始,假设第一个元素已经排好序。
  2. 取当前元素,与已排好序的部分从后向前比较,如果当前元素比前一个元素小,则将前一个元素后移,直到找到适当的位置,将当前元素插入其中。
  3. 重复步骤 2,直到所有元素都插入已排序部分,整个数组排序完成。

伪代码:

  1. 初始化已排序部分,设第一个元素为已排序部分。
  2. 对于第 i 个元素,查找其在前 i-1 个元素中的正确位置。
  3. 将元素插入该位置,并将其余元素后移。

插入排序算法的C代码

void InsertSort(int* a, int n) //接收一个数组a和数组的大小n
{for (int i = 0; i < n - 1; i++)  // 外层循环,从第1个元素开始,逐个插入到已排序部分{int end = i;  // 已排序部分的最后一个元素的索引为iint tmp = a[end + 1];  // tmp保存当前要插入的元素(未排序部分的第一个元素)// 内层循环,用于在已排序部分找到插入tmp的位置while (end >= 0){if (tmp < a[end])  // 如果当前已排序的元素比tmp大{a[end + 1] = a[end];  // 把a[end]向后移动一位,为tmp腾出位置}else{break;  // 如果找到比tmp小或相等的元素,则退出循环}--end;  // 继续往前检查前一个元素}// 当内层循环结束时,end的位置已经是比tmp小的元素,所以tmp应该插入到end+1的位置a[end + 1] = tmp;  // 将tmp插入到正确的位置}
}

for (int i = 0; i < n - 1; i++) 中的 i < n - 1 是因为插入排序的外层循环控制未排序部分的起始位置。具体原因如下:

  • 在每一次外层循环中,插入排序都会将当前元素(即 a[i + 1])插入到前面已经排好序的部分。
  • i 达到 n - 1 时,i + 1 就等于 n,这个位置已经超出了数组范围,且所有元素已经排序完毕,所以不需要继续循环。

因此,循环运行到 i < n - 1 就足够保证所有元素都被正确插入排序。

举例分析

我们用一个具体的例子来详细讲解插入排序的过程。假设数组为:

初始数组: [5, 2, 9, 1, 5, 6]

第一步(i = 0)

  • 已排序部分: [5]
  • 未排序部分: [2, 9, 1, 5, 6]
  • tmp = 2(即当前要插入的元素)

我们比较tmp和已排序部分的元素:

  • 2 < 5,因此将5向后移动,数组变为[5, 5, 9, 1, 5, 6]
  • tmp插入到位置0,数组变为[2, 5, 9, 1, 5, 6]

第二步(i = 1)

  • 已排序部分: [2, 5]
  • 未排序部分: [9, 1, 5, 6]
  • tmp = 9

比较tmp与已排序部分:

  • 9 > 5,直接插入,数组保持不变: [2, 5, 9, 1, 5, 6]

第三步(i = 2)

  • 已排序部分: [2, 5, 9]
  • 未排序部分: [1, 5, 6]
  • tmp = 1

比较tmp与已排序部分:

  • 1 < 9,将9向后移动,得到[2, 5, 9, 9, 5, 6]
  • 1 < 5,将5向后移动,得到[2, 5, 5, 9, 5, 6]
  • 1 < 2,将2向后移动,得到[2, 2, 5, 9, 5, 6]
  • tmp插入到位置0,得到[1, 2, 5, 9, 5, 6]

第四步(i = 3)

  • 已排序部分: [1, 2, 5, 9]
  • 未排序部分: [5, 6]
  • tmp = 5

比较tmp与已排序部分:

  • 5 < 9,将9向后移动,得到[1, 2, 5, 9, 9, 6]
  • 5 == 5,插入tmp到位置3,得到[1, 2, 5, 5, 9, 6]

第五步(i = 4)

  • 已排序部分: [1, 2, 5, 5, 9]
  • 未排序部分: [6]
  • tmp = 6

比较tmp与已排序部分:

  • 6 < 9,将9向后移动,得到[1, 2, 5, 5, 9, 9]
  • 6 > 5,插入tmp到位置5,得到[1, 2, 5, 5, 6, 9]

最终结果

[1, 2, 5, 5, 6, 9]

插入排序通过不断将元素插入到已排序部分的合适位置,逐步形成有序数组。

插入排序的复杂度分析

● 时间复杂度:
○ 最好情况:O(n),当数组已经有序时,只需对每个元素进行一次比较。
○ 最坏情况:O(n²),当数组是反序的,插入每个元素时都要遍历已排序部分的所有元素。
○ 平均情况:O(n²),通常情况下,每个元素需要和已排序部分的 n/2 个元素进行比较。

● 空间复杂度:O(1),只需要一个额外的变量 key 来存储待插入的元素,因此插入排序的空间复杂度是常数级的。

插入排序的优点

● 简单易实现:代码实现较为简单,适合少量数据的排序。
● 稳定性:插入排序是一个稳定的排序算法,意味着如果两个元素相等,它们的相对顺序不会在排序过程中改变。
● 适用场景:在数据量较小或者大部分数据已经有序时,插入排序的表现非常好。也适用于顺序存储和链式存储的线性表。

希尔排序

希尔排序(Shell Sort)详解

希尔排序是插入排序的改进版本,它通过逐步缩小步长进行排序,最终达到整体有序。希尔排序又称为缩小增量排序,由计算机科学家 Donald Shell 于 1959 年提出。

希尔排序的基本思想:

  1. 步长(Gap):希尔排序的核心思想是将待排序的数组按一定的步长(增量)分成多个子序列,分别对每个子序列进行插入排序。步长从较大逐渐减小,直至最终为 1。
  2. 分组排序:开始时,按较大的步长将数组分为若干个子数组,然后对这些子数组分别进行插入排序。随着步长的减小,子数组变得越来越大,直到步长为 1,整个数组被排序为一个有序序列。
  3. 插入排序:在每次分组后,对每个子数组应用插入排序。与直接插入排序不同的是,这些子数组并不是相邻的,而是由指定步长决定的。

希尔排序的步骤:

  • 初始步长:选择一个步长 gap,通常取数组长度的一半。
  • 分组排序:将数组按照 gap 分组,分别对每组元素进行插入排序。
  • 缩小步长:逐步缩小步长,继续对缩小后的分组进行插入排序。
  • 最终步长为 1:当步长为 1 时,整个数组进行一次标准的插入排序,完成最终的排序。

希尔排序的过程示例:

假设我们对数组 [13, 7, 9, 2, 5, 1, 6, 12, 11] 进行希尔排序,按以下步骤进行:

  1. 选择初始步长 gap = 4
    • 我们将数组分为 4 组:[13, 5][7, 1][9, 6][2, 12][11]
    • 分别对每组进行插入排序。
    • 排序后,数组为 [5, 1, 6, 2, 13, 7, 9, 12, 11]
  2. 缩小步长 gap = 2
    • 我们将数组分为 2 组:[5, 6, 13, 9][1, 2, 7, 12, 11]
    • 分别对每组进行插入排序。
    • 排序后,数组为 [5, 1, 6, 2, 9, 7, 13, 12, 11]
  3. 最终步长 gap = 1
    • 此时相当于对整个数组进行一次插入排序。
    • 最终结果为 [1, 2, 5, 6, 7, 9, 11, 12, 13]

希尔排序的C语言实现

void ShellSort(int* a, int n)
{int gap = n;  // 初始化gap为数组长度n,表示最初的间隔大小while (gap > 1)  // 当gap大于1时,不断进行循环{gap = gap / 2;  // 每次将gap减半,逐渐缩小间隔// 针对每个分组进行插入排序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];  // 保存待插入的元素// 插入排序过程:将tmp插入到其正确的位置while (end >= 0) {if (tmp < a[end]) {a[end + gap] = a[end];  // 如果tmp小于当前元素,将当前元素向后移动gap个位置end -= gap;  // 将end向前移动gap个位置,继续比较}else {break;  // 找到位置,跳出循环}}a[end + gap] = tmp;  // 将tmp插入到正确的位置}}}
}

i < n - gap 的条件是为了确保 i + gap 不会超出数组的范围。因为在 Shell 排序中,我们会用 a[i + gap] 来与前面的元素进行比较和移动。

举个例子:

假设数组长度 n = 8,当前 gap = 4

  • 初始 j = 0i = 0
  • i < n - gap = 8 - 4 = 4 成立时,i 的值可以是 0, 4

如果 i 达到 4,那么 i + gap = 4 + 4 = 8,这正好是数组的最后一位(超出范围)。

所以,限制条件 i < n - gap 可以确保 i + gap 仍然在数组范围内,避免越界访问。

举例分析:

我们用一个大小为8的数组:{8, 5, 7, 3, 2, 6, 4, 1},详细讲解希尔排序的过程。

初始状态

数组:{8, 5, 7, 3, 2, 6, 4, 1}
长度 n = 8

第一步:确定初始 gap

  • gap = n / 2 = 4(第一次间隔为4)

gap = 4 进行分组和排序

此时数组被分为4个子序列,分别是:

  • 序列1: 8, 2 (索引0和4)
  • 序列2: 5, 6 (索引1和5)
  • 序列3: 7, 4 (索引2和6)
  • 序列4: 3, 1 (索引3和7)

现在我们对每个子序列进行插入排序。

对第一个子序列 {8, 2} 插入排序

  • j = 0(第一个分组的起始索引)
  • i = j = 0
  • end = 0tmp = a[end + gap] = a[4] = 2
  • 比较 tmp (2)a[end] (8)
    • 2 < 8,因此 a[end + gap] = a[0 + 4] = a[4] 被替换为 8
    • 数组变为 {8, 5, 7, 3, 8, 6, 4, 1}
    • end 继续向前移动4个位置变成 -4,停止移动。
  • tmp (2) 放到 end + gap = 0,数组变为 {2, 5, 7, 3, 8, 6, 4, 1}

对第二个子序列 {5, 6}

  • j = 1i = 1
  • end = 1tmp = a[end + gap] = a[5] = 6
  • 比较 tmp (6)a[end] (5)6 > 5,无需移动。
  • 数组保持不变:{2, 5, 7, 3, 8, 6, 4, 1}

对第三个子序列 {7, 4}

  • j = 2i = 2
  • end = 2tmp = a[end + gap] = a[6] = 4
  • 比较 tmp (4)a[end] (7)4 < 7
    • a[end + gap] = a[6] = 7,数组变为 {2, 5, 7, 3, 8, 6, 7, 1}
    • end 继续向前移动4个位置,变成 -2,停止移动。
  • tmp (4) 放到 end + gap = 2,数组变为 {2, 5, 4, 3, 8, 6, 7, 1}

对第四个子序列 {3, 1}

  • j = 3i = 3
  • end = 3tmp = a[end + gap] = a[7] = 1
  • 比较 tmp (1)a[end] (3)1 < 3
    • a[end + gap] = a[7] = 3,数组变为 {2, 5, 4, 3, 8, 6, 7, 3}
    • end 继续向前移动4个位置,变成 -1,停止移动。
  • tmp (1) 放到 end + gap = 3,数组变为 {2, 5, 4, 1, 8, 6, 7, 3}

第二步:更新 gap

  • gap = gap / 2 = 2

gap = 2 进行分组和排序

此时数组被分为两个子序列,每间隔2个元素组成一组:

第一组:**{2, 4, 8, 7}**

  • 插入排序过程:
    • i = 0, end = 0, tmp = 4,无需调整。
    • i = 2, end = 2, tmp = 8,无需调整。
    • i = 4, end = 4, tmp = 7,无需调整。

数组保持 {2, 5, 4, 1, 8, 6, 7, 3}

第二组:{5, 1, 6, 3}

  • 插入排序过程:
    • i = 1, end = 1, tmp = 1
      • 1 < 5,将 5 向后移动,数组变为 {2, 5, 4, 5, 8, 6, 7, 3}
      • 插入 1,得到 {2, 1, 4, 5, 8, 6, 7, 3}
    • i = 3, end = 3, tmp = 6,无需调整。
    • i = 5, end = 5, tmp = 3
      • 3 < 6,将 6 向后移动,数组变为 {2, 1, 4, 5, 8, 6, 7, 6}
      • 插入 3,得到 {2, 1, 4, 3, 8, 5, 7, 6}

第三步:更新 gap

  • gap = gap / 2 = 1,此时进行标准插入排序。

gap = 1 进行排序

数组:{2, 1, 4, 3, 8, 5, 7, 6}

  • 对每个元素进行插入排序:
    • i = 1, tmp = 1,插入后 {1, 2, 4, 3, 8, 5, 7, 6}
    • i = 2, tmp = 4,无需调整
    • i = 3, tmp = 3,插入后 {1, 2, 3, 4, 8, 5, 7, 6}
    • i = 4, tmp = 8,无需调整
    • i = 5, tmp = 5,插入后 {1, 2, 3, 4, 5, 8, 7, 6}
    • i = 6, tmp = 7,插入后 {1, 2, 3, 4, 5, 7, 8, 6}
    • i = 7, tmp = 6,插入后 {1, 2, 3, 4, 5, 6, 7, 8}

最终排序结果

{1, 2, 3, 4, 5, 6, 7, 8}

希尔排序的复杂度分析:

  1. 时间复杂度
    • 希尔排序的时间复杂度依赖于选择的步长序列。一般使用的步长序列是 gap = n/2, n/4, ..., 1
    • 最坏情况时间复杂度:O(n²)(当步长序列选择不当时,可能退化为插入排序)。
    • 最好情况时间复杂度:O(n)(当数组已部分有序时)。
    • 平均时间复杂度:O(n^1.5),在实际应用中往往比 O(n²) 的排序算法快。
  2. 空间复杂度
    • 希尔排序是一种原地排序算法,只需常数级别的额外空间,即 O(1)。
  3. 稳定性
    • 希尔排序是不稳定的,因为相隔较远的元素可能会交换位置,破坏了相同元素之间的相对顺序。

希尔排序的优点和缺点

  • 优点
    • 相比于直接插入排序,希尔排序能够显著减少元素的移动次数,尤其是在数据量较大时表现更为出色。
    • 希尔排序在最坏情况下比 O(n²) 的算法快很多,尤其是接近有序的数据集。
  • 缺点
    • 希尔排序是不稳定的排序算法。
    • 其性能依赖于步长的选择,不同的步长序列会有不同的表现,且最优的步长序列是一个开放性问题。

  1. 📜 [ 声明 ] 由于作者水平有限,本文有错误和不准确之处在所难免,
  2. 本人也很想知道这些错误,恳望读者批评指正!
  3. 我是:勇敢滴勇~感谢大家的支持!

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

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

相关文章

S3C2440定时器

ee一、构造 二、设置相关位 1、MPLLCON寄存器&#xff08;配置MPLL寄存器&#xff0c;进行倍频&#xff09; 根据下列表格的想要输出的频率进行选择&#xff0c;选择完毕之后&#xff0c;对该寄存器进行设置 2、时钟分频控制&#xff08;CLKDIVN&#xff09;寄存器 根据不…

CSP-J 2024 入门组初赛第一轮初赛试题及答案解析

CSP-J 2024 入门组初赛第一轮初赛试题及答案解析 一、 单项选择题&#xff08;共15题&#xff0c;每题2分&#xff0c;共计30分&#xff1a;每题有且仅有一个正确选项&#xff09; 1 32 位 int 类型的存储范围是&#xff08; &#xff09; A -2147483647 ~ 2147483647 B -21…

第十四章:html和css做一个心在跳动,为你而动的表白动画

💖 让心跳加速,传递爱意 💖 在这个特别的时刻,让爱在跳动中绽放!🌟 无论是初次相遇的心动,还是陪伴多年的默契,我们的心总在为彼此跳动。就像这颗炙热的爱心,随着每一次的跳动,传递着满满的温暖与期待。 在这个浪漫的季节,让我们一同感受爱的律动!无论你是在…

【深度学习】(4)--卷积神经网络

文章目录 卷积神经网络一、画面不变性二、图像识别三、卷积网络结构1. 原理2. 卷积层3. 池化层4. 全连接层 四、感受野 总结 卷积神经网络 卷积神经网络&#xff08;Convolutional Neural Network&#xff0c;简称CNN&#xff09;是一种深度学习模型&#xff0c;特别适用于处理…

基于SpringBoot+Vue+MySQL的校园一卡通系统

系统展示 用户前台界面 管理员后台界面 系统背景 随着现代社会的快速发展&#xff0c;校园一卡通已成为大学生活中不可或缺的一部分。它不仅承载着校园消费的功能&#xff0c;还集成了学生身份证明、图书馆借阅、门禁系统等多种服务。然而&#xff0c;传统的一卡通管理系统往往…

设计模式之策略模式例题

答案&#xff1a;A 知识点&#xff1a; 策略模式又叫模板方法模式 它的意图是定义一个操作中的算法骨架。而将一些步骤延迟到子类中&#xff0c;使得子类可以不改变一个算法的结构即可重新定义算法的某些特定步骤

Elasticsearch——介绍、安装与初步使用

目录 1.初识 Elasticsearch1.1.了解 ES1.1.1.Elasticsearch 的作用1.1.2.ELK技术栈1.1.3.Elasticsearch 和 Lucene1.1.4.为什么不是其他搜索技术&#xff1f;1.1.5.总结 1.2.倒排索引1.2.1.正向索引1.2.2.倒排索引1.2.3.正向和倒排 1.3.Elasticsearch 的一些概念1.3.1.文档和字…

大模型LLM对话模拟器Dialogue Simulator Visualization可视化工具

伴随着生成式人工智能技术发展&#xff0c;进2年涌现出大语言模型LLM/Agent系统/AI推理等众多方向的技术项目和论文。其中对话系统&#xff0c;智能体交互是用户通过UX界面和AI系统进行交互&#xff0c;这种交互有时候也是多模态&#xff08;用户输入文字/语音/图像&#xff09…

MySQL高阶1919-兴趣相同的朋友

题目 请写一段SQL查询获取到兴趣相同的朋友。用户 x 和 用户 y 是兴趣相同的朋友&#xff0c;需满足下述条件&#xff1a; 用户 x 和 y 是朋友&#xff0c;并且用户 x and y 在同一天内听过相同的歌曲&#xff0c;且数量大于等于三首. 结果表 无需排序 。注意&#xff1a;返…

用最通俗易懂的语言和例子讲解三维点云

前言&#xff1a; 我整体的学习顺序是看的按B站那“唯一”的三维点云的视频学习的&#xff08;翻了好久几乎没有第二个...&#xff09;对于深度学习部分&#xff0c;由于本人并没有进行学习&#xff0c;所以没有深究。大多数内容都进行了自己的理解并找了很多网络的资源方便理解…

论文阅读:A Generalization of Transformer Networks to Graphs

论文阅读&#xff1a;A Generalization of Transformer Networks to Graphs 1 摘要2 贡献Graph TransformerOn Graph Sparsity&#xff08;图稀疏&#xff09;On Positional Encodings&#xff08;位置编码&#xff09;3 Graph Transformer Architecture&#xff08;架构&#…

阿里HPN-用于大型语言模型训练的数据中心网络

阿里巴巴HPN:用于大型语言模型训练的数据中心网络 探索大规模语言模型训练新方法&#xff1a;阿里巴巴HPN数据中心网络论文。 摘要 本文介绍了阿里云用于大型语言模型(LLM)训练的数据中心网络HPN。由于LLM和一般云计算之间的差异(例如&#xff0c;在流量模式和容错性方面)&…

一份热乎的阿里25届数据分析面试题

目录 阿里巴巴25届数分面试题 想要获取答案&#xff0c;想进一步了解SQL这门艺术语言的&#xff0c;可以订阅我的专栏数字化建设通关指南&#xff0c;将在该专栏进行详细解析。 专栏 原价99&#xff0c;现在活动价39.9&#xff0c;按照阶梯式增长&#xff0c;还差3个名额将上…

如何备份SqlServer数据库

第一步&#xff1a;登录你要备份的服务器数据库ssms 第二步&#xff1a;选择你要备份的数据库 此处已PZ-SJCS 数据库为例 右键该数据库-->任务-->备份 第三步&#xff1a;选择你备份的类型备份组件等&#xff0c;目标磁盘 &#xff0c;点击添加选择将你备份的文件备份那…

kubernetes网络(一)之calico详解

摘要 本文介绍Kubernetes最流行的网络解决方案calico。 kubernetes中不同宿主上的pod需要相互通信&#xff0c;如果按TCP/IP协议分层进行分类&#xff1a; 二层方案&#xff1a;flannel的udp和vxlan模式 三层方案&#xff1a;flannel的host-gw模式&#xff1b;calico的IPIP模…

pod介绍与配置

1、pod概念介绍 Pod 是 kubernetes 基本调度单位。每个 Pod 中可以运 行一个或多个容器&#xff0c;共享 Pod 的文件系统、IP 和网络等资源&#xff0c;每个 Pod 只有一个 IP。 2、使用 yaml或json 文件创建 Pod 声明式文件方式创建 Pod&#xff0c;支持 yaml 和 json 1&…

【Fastapi】参数获取,json和query

【Fastapi】参数获取&#xff0c;json和query 前言giteegithub query形式json传递同步方法使用json 前言 花了半个月的时间看了一本小说&#xff0c;懈怠了…今天更新下fastapi框架的参数获取 gitee https://gitee.com/zz1521145346/fastapi_frame.git github https://git…

【网络通信基础与实践番外一】多图预警之图解UDP和TCP前置知识

参考大佬的文章https://www.cnblogs.com/cxuanBlog/p/14059379.html 一、宏观架构中的传输层 在计算机中&#xff0c;任何一个可以交换信息的介质都可以称为端系统。计算机网络的运输层则负责把报文从一端运输到另一端&#xff0c;运输层实现了让两个互不相关的主机进行了逻辑…

【洛谷】P10417 [蓝桥杯 2023 国 A] 第 K 小的和 的题解

【洛谷】P10417 [蓝桥杯 2023 国 A] 第 K 小的和 的题解 题目传送门 题解 CSP-S1 补全程序&#xff0c;致敬全 A 的答案&#xff0c;和神奇的预言家。 写一下这篇的题解说不定能加 CSP 2024 的 RP 首先看到 k k k 这么大的一个常数&#xff0c;就想到了二分。然后写一个判…

Netty系列-4 Pipeline和Handler

背景 Netty将IO事件按照流向划分为两个部分&#xff1a;Inbound入站事件和Outbound出站事件。入站事件由外部触发&#xff0c;包括通道注册(register)、通道激活(active)、数据可读(read)、通道异常(exceptionCaught)等&#xff1b;出站事件由程序主动触发&#xff0c;如连接的…