插入排序以及希尔排序; 先学会插入,希尔会更简单喔

1.前言

  1. 首先肯定是要学会插入排序再学习希尔排序会更简单,因为代码部分有很多相似之处;如果你觉得你很强,可以直接看希尔排序的讲解。
  2. 哈哈哈!,每天进步一点点,和昨天的自己比

2.插入排序

  1. 让我们先来看看插入排序的动图,可能第一遍看不懂,最好结合解释一起看

  1. 核心思想:end + 1;去0 到 end 之前去找,如果tmp(end+1)小于end位置,end位置往后挪到end + 1位置上
  2. 挪动后tmp要继续找0 到 end的其他位置,如果tmp 比end位置大,就插入在end + 1的位置
  3. 需要注意的是:假如tmp大于0到end之间的某一个数据,直接跳出循环,在while循环外面插入
  4. for循环的i < n - 1为什么是这样? 因为是拿end + 1的位置和前面的比较;
    1. 假设 i < n;那你执行代码的时候,最后一个是end是n - 1位置,那么你要取tmp位置的就会导致越界,这个tmp就会取到n位置上的数,不就越界了吗
    2. 0 到 end 之间的区间数据是逐渐增长的,最开始是 0 到 1,第二次范围就是0 到 2
//插入排序 核心思想:end + 1;去0 end 之前去找,如果tmp小于end位置,就往end位置往后挪
void InsertSort(int* arr, int n)
{//n的位置不可访问,所以要 < n; < n - 1为什么? 因为是拿end + 1的位置和前面的比较for (int i = 0; i < n - 1; i++){int end = i;int tmp = arr[end + 1];while (end >= 0)//去查找出0 end范围的值{if (tmp < arr[end]){arr[end + 1] = arr[end];//如果tmp小于end位置的值,就向后挪,知道大于end位置的值就可以停下插入end--;}else{break;//此时说明tmp大于end位置,在end+1位置插入}}arr[end + 1] = tmp;//防止都比0 end之间要小,小于0跳出循环,-1 + 1;刚好是第一个位置}
}
  1. 下面说一种常规思路的写法,这样写可以,不过上面的写法更好
  2. 而且最主要就是防止tmp比 0 到 end 区间内的值都要小

void InsertSort(int* arr, int n)
{//n的位置不可访问,所以要 < n; < n - 1为什么? 因为是拿end + 1的位置和前面的比较for (int i = 0; i < n - 1; i++){int end = i;int tmp = arr[end + 1];while (end >= 0)//去查找出0 end范围的值{if (tmp < arr[end]){arr[end + 1] = arr[end];//如果tmp小于end位置的值,就向后挪,知道大于end位置的值就可以停下插入end--;}else{arr[end + 1] = tmp;break;//此时说明tmp大于end位置,在end+1位置插入}}if(end == -1) {arr[end + 1] = tmp;}

2.1插入排序的时间复杂度

  1. 最坏情况:
    1. O(N^2);当排序为逆序时,你想想0 到 end位置的数,假设是升序;竟然是升序但是end + 1这个数据是比0 到 end位置的数还要小那么就会交换很多次
    2. 假如上述的情况每次都这样呢?,那不是最坏的情况了
    3. 但是逆序的概率很低,反而乱序的概率大些
  2. 最好情况:O(N),已经非常接近有序了
  3. 还有一点,主要是插入排序适应能力强,在查找的过程中可以直接插入数据,比冒泡排序省去很多查找
  4. 这里也顺便提一下冒泡排序吧,做个比较

2.2冒泡排序

  1. 最坏情况:O(N^2);
  2. 最好情况:O(N),已经有序了
  3. 虽然冒泡和插入排序时间复杂度都是一样的,但是在同一所学校,同一个公司;但是它两之间仍然有好坏之分
  4. 为啥呢? 因为冒泡排序的最坏情况很容易达成,几乎每次交换都是一个等差数列,最好情况的情况概率太低了
  5. 竟然这么菜,有啥用呢 ? 实践中没有意义;适合教学
//冒泡排序
void BubblSort(int* arr, int sz)
{for (int i = 0; i < sz - 1; i++){int flag = 0;//假设这一趟没有交换已经有序for (int j = 0; j < sz - i - 1; j++){if (arr[j] > arr[j + 1]){int tmp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = tmp;flag = 1;//发生交换,无序}}if(flag == 0)//经过一趟发现有序,没有交换break;}
}

3.希尔排序

  1. 希尔排序分为两步
    1. 预排序(让数组接近有序)
    2. 插入排序
  2. 插入排序思想不错,可不可以优化一下,变得更好,甚至更强呢?
  3. 插入排序就是怕逆序的情况,所以通过预排序让数组接近有序,让大的值跳gap步可以很快的到后面
  4. 那么什么是gap,gap就是把数据分为gap组,每个数据间隔为gap的

3.1预排序

  1. 下面这样图,看不懂没关系细细给你分析。可以看到gap = 3,分成了红绿蓝三组数据
  2. 每组的每个数间隔为gap,假如说红色这一组它有4个数,其实你可以看成插入排序的一趟排序,只不过就是间隔为gap而已
  3. 而下面这样图就是,每组数据都进行一趟插入排序,可以自己画图试试看看,一不一样;当然下面也有动图的解释

3.2单趟排序

  1. 其实和插入排序思路差不多,只不过是end位置 和 tmp(end + gap)位置进行比较
  2. 如果tmp值小于end位置的值,就让end位置移动到 end + gap位置
  3. break跳出证明tmp大于 arr[end],此时tmp就放到end + gap位置即可
  4. 放在while循环外,和上面讲的插入排序的解决方法一样,当小于0 到 end时;防止越界
    1. 动图来解释一下,这个动图忘记设置循环了,所以要观看就重进一下

讲完单趟排序,再来说说应该注意的点

  1. 使用for来完成一趟排序的结束条件
  2. 红色这组举例:因为你每次跳gap步,如果 i 循环到了 数字为4的位置,此时进入循环,int tmp = arr[end + gap];这里面的 + gap会越界

最后是for循环是单趟的代码

for (int i = j; i < n - gap; i += gap){int end = i;//对应一趟排序的第几次int tmp = arr[end + gap];while (end >= 0)//比较交换{//如果小于arr[end]挪到arr[end + gap]if (tmp < arr[end]){arr[end + gap] = arr[end];}else{break;}}arr[end + gap] = tmp;}

3.3每组都进行预排序

  1. 外面再放一层for循环是什么意思呢?
  2. 意思是通过分组来排序,先是红色然后绿色蓝色,当j = 0是就是红色这一组
//希尔排序的预排序
void ShellSort(int* arr, int n)
{//假设gap为3int gap = 3;for (int j = 0; j < gap; j++){for (int i = j; i < n - gap; i += gap){int end = i;//对应一趟排序的第几次int tmp = arr[end + gap];while (end >= 0)//比较交换{//如果小于arr[end]挪到arr[end + gap]if (tmp < arr[end]){arr[end + gap] = arr[end];}else{break;}}arr[end + gap] = tmp;}}
}
  1. 当然上面那一种在外面再放一层for也可以
  2. 这里我们可以优化一下,变成两层循环;下面是优化后一小段过程

  1. 上面的是一组一组排序,而这里是多组一起排序
  2. 而通过整过程发现,红色会预排序3次,绿色2次,蓝色2次;和优化后的总次数一样 ,假设n = 10,10 - 3 = 7;刚好是7次了
  3. 这里并不能通过数循环来判断时间复杂,上面三层循环和这里的两次循环一样的
  4. 执行次数一样,排序的过程不一样

3.3.1优化后的代码

void ShellSort(int* arr, int n)
{//假设gap为3int gap = 3;for (int i = 0; i < n - gap; i++){int end = i;//对应一趟排序的第几次int tmp = arr[end + gap];while (end >= 0)//比较交换{//如果小于arr[end]挪到arr[end + gap]if (tmp < arr[end]){arr[end + gap] = arr[end];end -= gap;}else{break;}}arr[end + gap] = tmp;}
}

3.4预排序要进行多次

  1. 这里不是直接预排序完,然后就直接排序;要进行多次预排序才行
  2. 那么gap取多少才好呢?,最早有人研究 gap = gap / 2;但是这么最后每组只有两个,这么分不太好
  3. gap = gap / 3 + 1,更加合理一点

  1. 这为什么 + 1,因为刚好可以避免被3整除的时候等于了0,带入值可以想象一下8 / 3 = 2 + 1;3 / 3 = 0 + 1;
  2. 也就是说大于3的数除最后都会小于3,+ 1保证最后一个是 1
  3. 下面就是进行多次预排序,让大的数据更快到后面,小的数据更快到前面
  4. 弄完预排序,也就完成整个希尔排序,因为gap == 1的时候就是插入排序了
//希尔排序
void ShellSort(int* arr, int n)
{int gap = n;while (gap > 1){//gap > 1 就是预排序//gap == 1 就是插入排序gap = gap / 3 + 1;for (int i = 0; i < n - gap; i++){int end = i;//对应一趟排序的第几次int tmp = arr[end + gap];while (end >= 0)//比较交换{//如果小于arr[end]挪到arr[end + gap]if (tmp < arr[end]){arr[end + gap] = arr[end];end -= gap;}else{break;}}arr[end + gap] = tmp;}}
}

3.5预排序总结

  1. gap越大,大的可以越快的跳到后面,小的可以更快跳到前面,但是就会不接近有序
  2. gap越小,跳的越慢,但是更加接近有序,gap == 1相当于插入排序了
  3. 所以为了结合两者优点,直接让gap慢慢变小

3.6希尔排序的时间复杂度

  1. 直接给出结论,希尔排序的时间复杂度是O(N^1.3);下面的分析仅供参考!!!
  2. 不过下面有稍微推了一下过程,可能不太好
    1. gap大 :数据量少,交换的次数少;
    2. gap小:数据量大,交换的次数多;
    3. gap表示多少组和数据之间的间隔(gap),gap大,组就多,每组数据就少,gap小 ,组就少,每组数据就多

4.总结

  1. 希尔排序的思想非常好,也非常奇妙,估计大部分普通人想不到这个方法;
  2. 当然可以学习到这么好的思路,也是很大的进步了;万一你以后也发明了一种很厉害的算法呢
  3. 如果你最近在苦恼要不要写博客,我的建议是一定写,个人感觉这个加分项还是很有必要握在手里的

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

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

相关文章

鸿蒙Ability Kit(程序框架服务)【UIAbility组件与UI的数据同步】

UIAbility组件与UI的数据同步 基于当前的应用模型&#xff0c;可以通过以下几种方式来实现UIAbility组件与UI之间的数据同步。 [使用EventHub进行数据通信]&#xff1a;在基类Context中提供了EventHub对象&#xff0c;可以通过发布订阅方式来实现事件的传递。在事件传递前&am…

Rustdesk 自建服务器教程

一、环境 阿里云轻量服务器、debian11 系统 二、服务端搭建 2.1、开放防火墙指定端口 TCP(21115, 21116, 21117, 21118, 21119)UDP(21116) 2.2、安装 rustdesk 服务器文件 在 github 下载页https://github.com/rustdesk/rustdesk-server/releases/&#xff0c;下载 rustde…

【自撰写,国际象棋入门】第1课、棋盘和棋子

第1课 棋盘和棋子 一、国际象棋的棋盘 国际象棋的棋盘为一8乘8的黑、白格相间的棋盘&#xff0c;8条竖线的编号分别为A-H&#xff0c;8条横线的编号分别为1-8&#xff0c;在记谱时用竖线编号横线编号的方式表示棋盘上的格子&#xff0c;例如a1格、h8格等.棋盘上有几条重要的大…

c++程序员为什么要做自己的底层库

五一期间&#xff0c;在家里翻到之前上学时候用的电脑和工作日志&#xff0c;粗略浏览一番&#xff0c;感慨10年岁月蹉跎&#xff0c;仍然没有找到自己技术方向的“道”。遂有感而发&#xff0c;写下此文。 算起来&#xff0c;接触软件开发也有10年时间了&#xff0c;最开始是…

Java——异常

1.什么是异常 将程序执行过程中发生的不正常行为称为异常。 常见的异常有&#xff1a;算数异常&#xff0c;空指针异常&#xff0c;数组越界异常 每一种异常都有对应的类对齐描述 为了对每一种异常进行管理&#xff0c;Java内部实现了一个对异常的体系结构 1. Throwable&#x…

CS2游戏30万挂箱账号被封,饰品市场要变天

Steam游戏平台上CS2的玩家在线人数常年位于第一位&#xff0c;即便偶尔会被爆款游戏挤下来&#xff0c;但一切都是暂时的。饰品交易作为CS2的重要组成部分&#xff0c;早已成为了维系游戏热度的不二法门。可相对应的&#xff0c;各种挂箱子的工作室及个人也孕育而生。 但近来V社…

【Docker学习】docker pull详细说明

docker pull是我们经常用到的一个命令。我们使用一些官方镜像&#xff0c;如MySql、Nginx等都需要用docker pull下载。不过不用的话&#xff0c;也可以。比如使用docker run&#xff0c;要是找不到镜像&#xff0c;会自动下载。 命令&#xff1a; docker image pull 描述&am…

Uniapp写一个简单的商品瀑布流界面+商品详情

最终效果&#xff1a; 整体内容比较简单&#xff0c;参考了一篇瀑布流文章和一篇商品详情文章随便修改整了下&#xff0c;主要是给想做这方便面的新人一个简单逻辑的展示&#xff08;其实我也是第一次写这个emmm&#xff09; 一.组件下载&#xff1a; uni-icon uni-goods-nav…

什么是ACP?

前言 ACP指的是应用程序控制平面&#xff0c;是微服务架构中的一个关键组成部分。它负责管理微服务架构中的各个微服务&#xff0c;包括服务发现和注册、负载均衡、服务路由、熔断和降级、配置管理等方面的功能。 A&#xff1a;可用性 所有请求都有响应。C&#xff1a;强一致…

[DDR5 Jedec 3-4] 模式寄存器 Mode Register MRR/MRW

依公知及经验整理,原创保护,禁止转载。 专栏 《深入理解DDR》 1. 概念 模式寄存器用于定义各种操作模式。在初始化过程中,可以通过重新执行MRS命令来更改模式寄存器的内容。即使用户只想修改模式寄存器变量的一个子集,在发出MRS命令时也必须编程所有变量。 只有当所有ban…

现实残酷!存款百万只是少数人的游戏,普通家庭能存多少?

近期&#xff0c;网络上掀起了一股关于普通家庭终身存款上限的热烈讨论。一位网友通过简单的算术方式提出了一个假设&#xff1a;如果一对夫妻每年收入15万&#xff0c;并成功将6万存入银行&#xff0c;那么从25岁步入社会至60岁退休&#xff0c;他们理论上能积累到210万的存款…

Go语言之GORM框架(四)——预加载,关联标签与多态关联,自定义数据类型与事务(完结篇)

前言 本来是想着写多表关系的&#xff0c;不过写了一半发现重复的部分太多了&#xff0c;想了想与其做一些重复性工作&#xff0c;不如把一些当时觉得抽象的东西记录一下&#xff0c;就当用一篇杂记完成专栏的最后一篇文章吧。 预加载 简单示例 预加载主要用于在多表关系中…

谷歌浏览器的平替,内置开挂神器,我已爱不释手!

油猴浏览器正式版是一款基于谷歌Chromium源码开发的浏览器&#xff0c;它集成了集成了强大的油猴扩展&#xff08;Tampermonkey&#xff09;&#xff0c;使得用户可以轻松安装各种脚本&#xff0c;从而增强网页浏览体验。提供了一个更加个性化和高效的浏览体验。 油猴扩展&…

git使用流程

1.下载git 搜索下载 2.注册github账号&#xff08;打开爬墙工具&#xff09; 创建一个仓库 3.配置邮箱和密码 4.所以找一个文件夹 鼠标右键 选择 open Git Bash here&#xff08;当前文件夹下打开命令行&#xff09; 输入命令 配置用户名和邮箱 5.将建的仓库克隆下来 …

【JS实战案例汇总——不定时更新版】

一&#xff1a;转换时间案例 1 需求&#xff1a; 用户输入秒数&#xff0c;系统会自动将秒数转变为小时、分钟、秒&#xff0c;并且不满10的要在前面补零 2 算法&#xff1a; 小时:hour parseInt(总秒数/60/60%24) 分钟:minute parseInt(总秒数/60%60) 秒数:second pa…

测试基础09:缺陷(bug)生命周期、定位方式和管理规范

课程大纲 1、缺陷&#xff08;bug&#xff09;生命周期 2、缺陷&#xff08;bug&#xff09;提交规范 2.1 宗旨 简洁、清晰、可视化&#xff0c;减少沟通成本。 2.2 bug格式和内容 ① 标题&#xff1a;一级功能-二级功能-三级功能_&#xff08;一句话描述bug&#xff1a;&…

---初始Linux---

一、认识计算机 计算机 硬件 软件 硬件&#xff1a;就是计算机系统中由电子、机械和光电元件等组成的各种物理装置的总称&#xff08;CPU\GPU\...&#xff09; 软件&#xff1a;是用户和计算机硬件之间及进行交流的工具 然而一个简单的计算机或者说基本的计算机就是有两大…

C++ A (1020) : 幂运算

文章目录 一、题目描述二、参考代码 一、题目描述 二、参考代码 #include<bits/stdc.h> using namespace std; typedef long long ll;void qq(ll a, ll b, ll m) {if (a 0) cout << 0 << endl;;ll out 1;a % m;while (b > 0){if (b & 1)//奇数的最…

lux和ffmpeg进行下载各大主流自媒体平台视频

1、lux下载&#xff0c;链接&#xff1a;https://pan.baidu.com/s/1WjGbouL3KFTU6LeqZmACpA?pwdagpp 提取码&#xff1a;agpp 2、ffmpeg下载&#xff0c;跟lux放在同一个目录&#xff1b; 3、为lux、ffmpeg设置环境变量&#xff1b; 4、WINR&#xff0c;打开运行&#xff0…

带你自学大语言模型系列 —— 前言

今天开始&#xff0c;我计划开启一个系列 《带你自学大语言模型》&#xff0c;内容也已经准备了一段时间了。 该系列的落脚点是“自学”和“大语言模型”&#xff0c;二者不分伯仲&#xff0c;这也是本系列和其他技术文章不一样的地方。 至于原因&#xff0c;我不想只做大语言…