当前位置: 首页 > news >正文

11 插入排序和希尔排序

1. 插入排序

基本思想
直接插入排序是一种简单的插入排序法,基本思想:

把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列

在玩扑克牌时,就用了插入排序的思想

在这里插入图片描述

过程
在这里插入图片描述

类似扑克牌,手里有3,7,8,9,,四张牌,这时,如果摸到4,怎么排它的位置。先和9比,小于9,再和前面的8比,再和7比,再和3比,这时大于3,就找到了它该插入的位置,将4放在3的后面,其他的往后挪一个

在这里插入图片描述

从7开始排,第一个数肯定是有序的。
然后是4,4小于7,所以7应该在4的前面,将7往后挪一位,4放在7的位置
5比7小,7往后挪,5比4大,所以5的位置就是4的后面

这样不断比较,直到最后一个数排完

void Sort(int ary[], int len)
{//第一个是有序的,从第二个开始for (int i = 1; i < len; i++){//[0,end]的区间是有序的,end是待排序数的前一个下标int end = i - 1;int temp = ary[i];while (end >= 0){if (ary[end] > temp){ary[end + 1] = ary[end];end--;}else{break;}}//此时,end是前一个位置ary[end + 1] = temp;}}

特性总结:
1.元素集合越接近有序,直接插入排序算法的时间效率越高
2.时间复杂度:最坏O(N2),逆序的时候最坏,最好是O(N),正序的时候
3.空间复杂度: O(1)
4.稳定性:稳定

插入和冒泡
两者在最好和最坏的情况下时间复杂度相同,但对于大部分有序,局部无序的情况下,插入的适应性更强
在这里插入图片描述

上面的数据只有最后面的9和8无序,对于冒泡排序来说,需要第一轮比过去,交换8和9,第二轮到9的位置后发现有序,排序完毕。插入排序来说,检查到8的时候,一交换,排序完毕。这时,插入排序的效率高。冒泡排序更容易理解,比较容易上手

2. 希尔排序

基本思想
希尔排序又称缩小增量法,基本思想是:先选定一个整数,把待排序文件中所有记录分成几个组,对每一组内的记录进行排序。然后缩小分组间隔。重复上述过程,当增量缩小到1时,所有记录排为一组,排好序

插入排序对大部分有序的数据效率很高,但对于倒序的效率很低。可以根据这个特性优化。先对数据用一种效率高的方法进行预排序,使数据接近有序。最后再进行一次插入排序,就可以排序成功

过程
在这里插入图片描述
首先将整个数组按间隔gap分组,分为gap组,这里以间隔3分组

在这里插入图片描述

上面的从9开始每间隔3分为归为一组,9,6,3,1分到了一组

然后从8开始分组,直到将所有数据分组完
在这里插入图片描述
以颜色区分分为了3组,首先写一个类似的插入排序对红色组排序

void ShellSort(int ary[], int len)
{
//间隔3int gap = 3;
//0,从end+gap开始往前对比,到组内最后一个数排完,每次加间距for (int i = 0; i < len - gap; i += gap){int end = i;int temp = ary[gap + end];
//将gap+end下标,也就是组内下一个数和前面的所有对比交换while (end >= 0){if (ary[end] > temp){ary[gap + end] = ary[end];end = end - gap;}else{break;}}ary[end + gap] = temp;}	
}

在这里插入图片描述
将红色组排好了序,接着将其他两组也排好序,意味着需要循环组数的次数,控制变量i

	int gap = 3;//循环gap组for (int j = 0; j < gap; j++){//从end+gap开始往前对比,找组内下一个数,每次加间距for (int i = j; i < len - gap; i += gap){int end = i;int temp = ary[gap + end];//将gap+end下标,也就是组内下一个数和前面的所有对比交换while (end >= 0){if (ary[end] > temp){ary[gap + end] = ary[end];end = end - gap;}else{break;}}ary[end + gap] = temp;}}}

在这里插入图片描述

上面的数字仍是无序的,但经过预排序已经大部分有序

上面的三层循环可以减到2层循环,最外层的循环可以去掉。实际上外面两层循环本质上是把所有数据遍历了一遍,所以可以只用一层,多组并排,i每次递增1

void ShellSort(int ary[], int len)
{int gap = 3;//从end+gap开始往前对比,找组内下一个数,每次加间距for (int i = 0; i < len - gap; i++){int end = i;int temp = ary[gap + end];//将gap+end下标,也就是组内下一个数和前面的所有对比交换while (end >= 0){if (ary[end] > temp){ary[gap + end] = ary[end];end = end - gap;}else{break;}}ary[end + gap] = temp;}
}

怎么让大部分有序的数据变为有序,只需要控制gap的变化,当gap=1时,就是插入排序。gap应该根据数据量变化,数据量大的时候,gap的跳跃变大,gap越大,越接近无序,但可以更快的将更大更小的数放在两端的位置

为了使排的数据最终变有序,gap的值最后必须可以变为1,官方给出的是gap每次/3+ 1,当gap为2时,就变为0,所以/3后再加1,这样无论多少数据,最终都会分为一组

void ShellSort(int ary[], int len)
{int gap = len;//gap>1预排序//gap=1插入排序while (gap > 1){//+1保证最后一次是1//gap = gap/2gap = gap / 3 + 1;//从end+gap开始往前对比,找组内下一个数,每次加间距for (int i = 0; i < len - gap; i++){int end = i;int temp = ary[gap + end];//将gap+end下标,也就是组内下一个数和前面的所有对比交换while (end >= 0){if (ary[end] > temp){ary[gap + end] = ary[end];end = end - gap;}else{break;}}ary[end + gap] = temp;}}}

时间复杂度
在这里插入图片描述
gap/3这一层时间复杂度log3N,因为每次都除以3。而对于里面的两个循环,时间复杂度计算比较麻烦,大致可认为是O(N)。因为gap每次都是变化的,且前面的排序对后面排序的增益效果无法估算
在这里插入图片描述
上面只能以最坏的情况计算,但gap越小,情况应该是越好的

特性
1.希尔排序是对直接插入排序的优化
2.当gap>1时是预排序,目的是让数组更接近有序。gap==1时,数组已经接近有序了,就会很快
3.时间复杂度: O(N1.3)
在这里插入图片描述

在这里插入图片描述

3.稳定性: 不稳定

3. 插入排序和希尔排序比较

随机生成10万个数字,计算两个排序所消耗的时间,大致差了100倍
在这里插入图片描述

相关文章:

  • 《Docker极简教程》--Docker环境的搭建--在Mac上搭建Docker环境
  • C语言使⽤ scanf()函数应注意的问题是什么?
  • 设计模式(结构型模式)桥接模式
  • linux的tree用法
  • 【每日一题】LeetCode——反转链表
  • HCIA-HarmonyOS设备开发认证V2.0-3.轻量系统内核基础
  • vue绘制语音波形图---wavesurfer.js
  • FPS游戏框架漫谈第二十二天
  • 【go】ent操作之CRUD与联表查询
  • uniapp /微信小程序 使用map组件实现手绘地图方案
  • office文件转pdf在线预览
  • 【前端高频面试题--Vue基础篇】
  • 多模态对比语言图像预训练CLIP:打破语言与视觉的界限,具备零样本能力
  • 猫头虎分享已解决Bug || 未定义的变量(Undefined Variable):ReferenceError: x is not defined
  • 获取旁站 / C 段:第三方网站(附链接)
  • Asm.js的简单介绍
  • canvas 高仿 Apple Watch 表盘
  • create-react-app做的留言板
  • Docker 1.12实践:Docker Service、Stack与分布式应用捆绑包
  • Laravel 菜鸟晋级之路
  • Linux编程学习笔记 | Linux多线程学习[2] - 线程的同步
  • PHP面试之三:MySQL数据库
  • Python 反序列化安全问题(二)
  • Python学习笔记 字符串拼接
  • Rancher如何对接Ceph-RBD块存储
  • socket.io+express实现聊天室的思考(三)
  • Swoft 源码剖析 - 代码自动更新机制
  • 测试开发系类之接口自动化测试
  • 从零到一:用Phaser.js写意地开发小游戏(Chapter 3 - 加载游戏资源)
  • 复习Javascript专题(四):js中的深浅拷贝
  • 入门到放弃node系列之Hello Word篇
  • 设计模式(12)迭代器模式(讲解+应用)
  • 一起参Ember.js讨论、问答社区。
  • 因为阿里,他们成了“杭漂”
  • 优秀架构师必须掌握的架构思维
  • 回归生活:清理微信公众号
  • 如何用纯 CSS 创作一个货车 loader
  • # Apache SeaTunnel 究竟是什么?
  • #if 1...#endif
  • #调用传感器数据_Flink使用函数之监控传感器温度上升提醒
  • (20)目标检测算法之YOLOv5计算预选框、详解anchor计算
  • (C语言版)链表(三)——实现双向链表创建、删除、插入、释放内存等简单操作...
  • (delphi11最新学习资料) Object Pascal 学习笔记---第8章第2节(共同的基类)
  • (MATLAB)第五章-矩阵运算
  • (pojstep1.1.1)poj 1298(直叙式模拟)
  • (二)springcloud实战之config配置中心
  • (附源码)php新闻发布平台 毕业设计 141646
  • (附源码)springboot 智能停车场系统 毕业设计065415
  • (四)linux文件内容查看
  • (四)库存超卖案例实战——优化redis分布式锁
  • (学习日记)2024.01.19
  • *p=a是把a的值赋给p,p=a是把a的地址赋给p。
  • .NET Core 网络数据采集 -- 使用AngleSharp做html解析
  • .Net IE10 _doPostBack 未定义
  • .NET 跨平台图形库 SkiaSharp 基础应用