直接插入排序和希尔排序

在这里插入图片描述

文章目录

  • 前言
  • 概述
  • 直接插入排序
    • 本质
    • 分析
    • 代码
    • 总结
  • 希尔排序
    • 一组一组排序
    • 多组同时进行
    • 完整的代码
  • 总结

前言

排序: 所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。

稳定性: 假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。

内部排序: 数据元素全部放在内存中的排序。

外部排序: 数据元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的排序。

概述

直接插入排序是一种简单的插入排序法,其基本思想是:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列 。

现实生活中,打扑克牌就是一个插入排序的例子
在这里插入图片描述

直接插入排序

本质

  1. 设一个待排序的数组,a[1]是一个有序的序列
  2. 将后面的元素和已经排序好的元素进行比较。

图解:
在这里插入图片描述

分析

本篇文章,以从小到大的顺序排序为例

以其中一趟排序为例:

在这里插入图片描述

前面的4 5 6已经是排序好的数字,现在需要将元素3继续排序。

此时的end指向6tmp指向的的是end+1的位置,即3

endtmp进行比较,显然3<6,即tmp<end

此时将tmp拿出来,将end移动到tmp位置

end继续往前移动,继续和tmp比较

在这里插入图片描述
此时依然tmp<endend还是需要往前移动,即end--

在这里插入图片描述
此时依然tmp<endend还是需要往前移动,即end--

在这里插入图片描述

此时end<0,循环结束,无需再比较,直接将tmp元素插入到最前面。

在这里插入图片描述

一趟排序的代码:

while (end >= 0){if (tmp < a[end]){a[end + 1] = a[end];--end;}else{break;}}

因此一趟排序的终止条件为end<0

要想完成所有元素排序,在一趟排序的基础上套一个for循环。从第一个元素开始遍历,end=i,循环结束的标志是i<n-1,此时end指向的的是倒数第二个元素,tmp指向最后一个元素。

代码

# define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<assert.h>void InsertSort(int*a,int n)
{// [0,end] end+1for (int i = 0; i < n - 1; i++){int end = i;int tmp = a[end + 1];while (end >= 0){if (tmp < a[end]){a[end + 1] = a[end];--end;}else{break;}}a[end + 1] = tmp;}
}//输出排序后的数组
void PrintArray(int* a, int n)
{for (int i = 0; i < n; i++){printf("%d ", a[i]);}printf("\n");
}int main()
{//测试int a[] = { 3, 2, 6, 8, 4, 6, 0, 9, 5, 7, 1 };InsertSort(a, sizeof(a) / sizeof(a[0]));PrintArray(a, sizeof(a) / sizeof(a[0]));return 0;
}

总结

直接插入排序适用于较为有序的元素

当元素较为有序时,时间复杂度为O(N)

当元素为逆序时,时间复杂度为O(N2)

希尔排序

希尔排序简单来说,可以分为两步,先进行分组排序,再进行插入排序。

先将整个待排序序列分割成几组,从而减少参与直接插入排序的数据量,对每组放分别进行直接插入排序,然后增加每组的数据量,重新分组。这样当经过几次分组排序后,整个序列中的记录“基本有序”时,再对整个序列进行一次直接插入排序。

一组一组排序

将待排序列9 8 7 6 6 5 4 3 2 1 0进行排序,先进行分组,小编以间隔3为一组,可得到以下分组方式:
在这里插入图片描述

红色的为一组排序,蓝色的为一组排序,绿色为一组排序

先对红色的一组排序,红色的序列为9 6 4 1,进行直接插入排序,即1 4 6 9

然后对蓝色的一组排序,蓝色的序列为8 6 3 0,进行直接插入排序,即0 3 6 8

最后对绿色的一组排序,绿色的序列为7 5 2,进行直接插入排序,即2 5 7

此时的整个序列的顺序为1 0 2 4 3 5 6 6 7 9 8,待排序序列是一个较为有序的序列,这时直接使用插入排序,降低了插入排序的复杂度

int i = 0, j = 0;int gap = 3;for (i = 0; i < gap; i++){for (j = i; j < n - gap; j = j + gap){int end = j;int tmp = a[end + gap];while (end >= 0){if (tmp < a[end]){a[end + gap] = a[end];end = end - gap;}elsebreak;}a[end + gap] = tmp;}}

j == 0时,红色组进行排序

j == 1时,蓝色组进行排序

j == 2时,绿色组进行排序

多组同时进行

多组同时进行,只需要一个for循环,不需要再嵌套循环。

只需要将j一开始指向第一个待排序元素(是红色组的第一个元素),然后j++,指向第二个元素(蓝色组第一个元素),再j++,此时指向第三个元素(绿色组第一个元素)。

for (j = 0; j < n - gap; j ++){int end = j;int tmp = a[end + gap];while (end >= 0){if (tmp < a[end]){a[end + gap] = a[end];end = end - gap;}elsebreak;}a[end + gap] = tmp;}

无论是一组一组进行还是多组同时进行,都只是一个预排序,并没有完成整个跑排序

完整的代码

gap越大,大的值可以更快的跳到后面,小的值可以更快的跳到前面,越不接近有序

gap越小,跳的越慢,但是越接近有序,如果gap==1就是直接插入排序

那么,gap的值到底应该是多少??这个问题官方也没有一个直接的解答,但是有一个操作,可以解决这个问题:
gap > 1时是预排序,目的让他接近有序
gap == 1是直接插入排序,目的是让他有序

首先将gap的置为待排序元素的大小n,让gap循环一次就除以2,无论gap的值是多少,最终都会等于1;或者gap/3+1,无论gap的值是多少,最终都会等于1。gap==1时,是直接插入排序,不会再进入循环。

图解:
在这里插入图片描述

ShellSort排序代码:

void ShellSort(int* a, int n)
{int gap = n;// gap > 1时是预排序,目的让他接近有序// gap == 1是直接插入排序,目的是让他有序while (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 (tmp < a[end]){a[end + gap] = a[end];end -= gap;}else{break;}}a[end + gap] = tmp;}}
}	

测试代码:

# define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>void ShellSort(int* a, int n)
{int gap = n;// gap > 1时是预排序,目的让他接近有序// gap == 1是直接插入排序,目的是让他有序while (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 (tmp < a[end]){a[end + gap] = a[end];end -= gap;}else{break;}}a[end + gap] = tmp;}}
}void PrintArray(int* a, int n)
{for (int i = 0; i < n; i++){printf("%d ", a[i]);}printf("\n");
}int main()
{int a[] = { 9,8,7,6,6,5,4,3,2,1,0 };ShellSort(a, sizeof(a) / sizeof(a[0]));PrintArray(a, sizeof(a) / sizeof(a[0]));return 0;
}

在这里插入图片描述

总结

希尔排序是在直接插入排序基础上完成的,希尔排序的时间复杂度的计算涉及数论,小编无法解读。直接排序的时间复杂度虽然是O(N2),但是不要小瞧它,比冒泡排序还是优秀很多。在冒泡排序直接插入排序,希尔排序中,希尔排序还是很优秀的。

在这里插入图片描述

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

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

相关文章

浅谈Java反射中的getFields()方法和getDeclaredFields ()方法

目录 1. 概念2. getFields()方法2. getDeclaredFields()方法4. 总结 1. 概念 反射是Java中一种强大的机制&#xff0c;允许在运行时获取、检查和操作类、方法、字段等信息&#xff0c;而不需要在编译时知道这些信息。 其中字段&#xff08;Field&#xff09;在Java中是类中用…

Unity向Web服务器上传和下载图片

Unity向Web服务器上传和下载图片 如果本片有看不懂的请查看我上篇文章&#xff1a;[Unity与Web服务器Post&#xff0c;Get](https://blog.csdn.net/qq_42194657/article/details/103031573)一、上传和下载图片1.在Unity中创建一个RawImage并在WebManager.cs脚本中添加一个Textu…

模式识别与机器学习-特征选择和提取

模式识别与机器学习-特征选择和提取 特征选择一些距离测度公式独立特征的选择准则一般特征的散布矩阵准则 离散K-L变换 谨以此博客作为复习期间的记录。 常见分类问题的流程&#xff0c;数据预处理和特征选择提取时机器学习环节中最重要的两个流程。这两个环节直接决定了最终性…

mac电脑系统性能检测:Geekbench 6最新 for mac

Geekbench 6是一款跨平台的性能测试软件&#xff0c;旨在全面衡量电脑在不同任务和应用程序中的性能。它适用于Windows、macOS、Linux等操作系统&#xff0c;以及移动设备如Android和iOS。 Geekbench 6主要特点包括&#xff1a; 全面覆盖各种性能测试领域&#xff0c;包括CPU…

CTF-PWN-栈溢出-中级ROP-【BROP-1】

文章目录 BROP(Blind ROP)攻击条件攻击路线三种gadget函数参数构造利用PLT构造rdx的参数的gadget找输出函数的plt输出二进制文件内容利用plt表中存在跳转到got表中相应的地址 BROP(Blind ROP) 即没有得到源码或者可执行程序文件的情况的文件下&#xff0c;只有一个提供的功能端…

C++学习实践(一)高频面试问题总结(附详细答案)

文章目录 一、基础常见面试题1、数组和链表区别2、深拷贝和浅拷贝相关问题的区别3、a和a区别4、c内存模型5、四种强制转换和应用场景 二、指针相关1、指针和引用的区别2、函数指针和指针函数3、传指针、引用和值4、常量指针和指针常量5、野指针6、智能指针的用法 三、关键字作用…

mySQL事务与存储引擎

目录 mySQL事务 1.事务的概念 2.事务的ACID特点 3.多客户端同时访问一个表时&#xff0c;出现的一致性问题 4.事务的隔离级别 5.事务的隔离级别作用范围 查询全局事务隔离级别 设置全局事务隔离级别 ​编辑查询会话事务隔离级别 设置会话事务隔离级别 6.事务控制语句…

这一次,我准备了 20节 PyTorch 中文课程

对于刚接触深度学习的小白来说&#xff0c;PyTorch 是必会的框架。 只是&#xff0c;很多小伙伴还没来得及开启学习之路&#xff0c;一个最重要的问题就摆在了面前&#xff1a; PyTorch&#xff0c;该怎么学呢&#xff1f; 很多同学会自己在网上找资料&#xff0c;不仅耗费时间…

如何使用IIS代理iServeriPortal

刘大 这里写目录标题 前言1.IIS使用http协议&#xff0c;iServer&iPortal使用http协议具体操作1.1 开启IIS管理器&#xff0c;并安装APR组件和url重写组件1.3 启用代理1.4 添加URL重写规则1.4.1 设置保留原始主机标头1.4.2 修改注册表1.5 iPortal的额外配置1.5.1 增加新的入…

使用防火墙是否可以应对DDoS攻击?

很多游戏行业公司对网络安全不够了解&#xff0c;觉得装个防火墙就可以万事大吉了。实际上使用防火墙确实是解决DDoS攻击问题的一种有效方法&#xff0c;一些更先进的防火墙还可以采用其他防御措施&#xff0c;例如:深度包检测、行为分析、人工智能等&#xff0c;来识别和防御各…

Vi文本编辑器如何使用,这里有详细步骤

Vi是一个强大的文本编辑器&#xff0c;包含在大多数Linux系统中&#xff0c;甚至是嵌入式系统中。有时&#xff0c;你必须在不包括更友好的文本编辑器的系统上编辑文本文件&#xff0c;因此了解Vi是至关重要的。 与Nano不同&#xff0c;Nano是一款易于使用的终端文本编辑器&am…

Shell 脚本应用(四)

正则表达式概述 正则表达式又称正规表达式&#xff0c;常规表达式。在代码中常简写为regex&#xff0c;regexp 或RE.正则表达式 是使用单个字符串来描述&#xff0c;匹配一系列符合某个句法规则的字符串&#xff0c;简单来说&#xff0c;是一种匹配字符串 的方法&#xff0c;通…

JVM入门到入土-Java虚拟机寄存器指令集与栈指令集

JVM入门到入土-Java虚拟机寄存器指令集与栈指令集 HotSpot虚拟机中的任何操作都需要入栈和出栈的步骤。 由于跨平台性的设计&#xff0c;Java的指令都是根据栈来设计的。不同平台CPU架构不同&#xff0c;所以不能设计为基于寄存器的。优点是跨平台&#xff0c;指令集小&#x…

PyCharm安装PyQt5一系列工具

目录 目录 一、安装IDE 二、安装Pycharm 三、Pycharm内安装一系列工具 1.新建一个项目 2.点击左上角File-Setting-点击加号 3.添加环境变量 4.配置qtdesigner,pyuic,pyrcc 三、用Pyinstaller打包成exe&#xff08;简单版&#xff09; 四、我的解释器装在了中文路径&a…

如何分析信号的频率信息?

1 简介 当信号的频率信息是已知的&#xff0c;或者近似已知的&#xff0c;则采样频率容易选择&#xff0c;选择最大采样频率的2倍以上(奈奎斯特采样)。 这里&#xff0c;将讨论信号频率未知的情况。尤其是&#xff0c;考虑如何避免混叠错误。 2 低通滤波(反混叠) 去除混叠错…

二、基于图像和结构化数据多模态融合的回归预测网络【框图讲解+源码】

整理读研期间做的项目与日常小实验 本篇未完待续…代码部分整理后补充 0. 背景 实验室有一些材料的SEM&#xff08;扫描电镜&#xff09;图像、也有对应的组分信息&#xff08;结构化数据&#xff0c;包含类别特征和连续的数值特征&#xff09;&#xff0c;以及对应的力学性能…

WEB 3D技术 three.js带着大家简单在文档上过一下集合体 并理解如何在文档中调试参数

前面讲了 自己创建集合体 顶点分组 但是这样其实挺麻烦的 three.js 其实已经给我们封装好了 官网直接搜索 geometry 这边这个立方体 应该算是我们用的最多的 这里 这个就是通过三个参数设置 它们分别对应 高度 宽度 厚度 拉下来 我们看到 其实他有六个参数 前面的 x y z三个…

有什么好用的C/C++源代码混淆工具?

​ 有什么好用的C/C源代码混淆工具&#xff1f; 开始使用ipaguard 前言 iOS加固保护是直接针对ios ipa二进制文件的保护技术&#xff0c;可以对iOS APP中的可执行文件进行深度混淆、加密。使用任何工具都无法逆向、破解还原源文件。对APP进行完整性保护&#xff0c;防止应用…

sqlmap各个命令的解释及其基本用法

各个命令的用法 -h,--help Show basic help message and exit(显示基本帮助消息并退出) -hh Show advanced help message and exit&#xff08;显示高级帮助信息并退出&#xff09; --version Show programs version number and exit&#xff08;显示程序的版本…

力扣刷题记录(18)LeetCode:474、518、377、322

目录 474. 一和零 518. 零钱兑换 II 377. 组合总和 Ⅳ 322. 零钱兑换 总结&#xff1a; 474. 一和零 这道题和前面的思路一样&#xff0c;就是需要将背包扩展到二维。 class Solution { public:int findMaxForm(vector<string>& strs, int m, int n) {vector&l…