【数据结构】8.2 插入排序

news2024/9/20 22:35:44

文章目录

  • 前言
  • 1. 直接插入排序
    • 直接插入排序算法
    • 直接插入排序性能分析
  • 2. 折半插入排序
  • 3. 希尔排序
    • 希尔排序算法
    • 希尔排序算法分析
  • 排序方法比较

前言

类似于俺们打牌时的插入,每抓来一张牌的时候,就将它放在合适的位置上,插入一张牌之后手里的牌仍然是有序排列的。

在这里插入图片描述

基本思想

  • 每步将一个待排序的对象,按其关键码大小,插入到前面已经排序号的一组对象的适当位置上,直到对象全部插入为止。
  • 即:边插入边排序,保证子序列中随时都是排好序的

基本操作:有序插入

  • 在有序序列中插入一个元素,保持序列有序,有序长度不断增加。
  • 起初,a[0] 是长度为 1 的子序列。然后,逐一将 a[1] 到 a[n-1] 插入到有序子序列中。

有序插入方法

  • 在插入 a[i] 前,数组 a 的前半段(a[0] ~ a[i-1])是有序段,后半段(a[i] ~ a[n-1])是停留于输入次序的无序段
  • 插入 a[i] 使 a[0] ~ a[i-1] 有序,也就是要为 a[i] 找到有序位置 j(0 <= j <= i),将 a[i] 插入在 a[j] 的位置上。

插入位置

  • 插在中间:要插入的 a[i] 元素比前 n 个大,比后 m 个小,将后 m 个元素都后移,将 a[i] 插进去。

在这里插入图片描述

  • 插在最前面:要插入的元素 a[i] 比最小的元素都要小比,那最前面的就是他的位置,将所有的元素都后移,将它插进去。

在这里插入图片描述

  • 插在最后面:要插入的元素比当前所有的已经有序的元素都大,原来的有序序列无序移动,直接在最后插入 a[i] 。

在这里插入图片描述

插入排序的种类

在这里插入图片描述

1. 直接插入排序

算法步骤

  1. 设待排序的记录存在数组 r[1…n] 中,r[1] 是一个有序序列。
  2. 循环 n-1 次,每次使用顺序查找法,查找 r[i](i = 2,…,n)在已排好序的序列 r[1…i-1]中的插入位置,然后将 r[i] 插入表长为 i-1 的有序序列 r[1…i-1],直接将 r[n] 插入表长为 n-1 的有序序列 r[1…n-1],最后得到一个表长为 n 的有序序列。

采用顺序查找法查找插入位置

在这里插入图片描述

  • 假设要将第 i 个位置上的元素插入到绿色的有序序列。
  • 查找它的插入位置,然后将插入位置之后的元素往后移动一位,最后才能插入进去。
  • 如果直接将后面的元素往后移动的话,4 号位置的元素就会先被覆盖,所以要先将待插入的值存起来。
  1. 复制插入元素:x = a[i];
  2. 记录后移,查找插入位置
    • 用一个新的变量来表示当前所查找的位置。
    • 用 a[j] 的值来和 x 进行比较,,j 的初始值为 i-1:
      • 如果 a[j] > x,说明 x 要插入在 a[j] 之前,然后 a[j] 需要往后移到 a[j+1] 的位置。
      • 一直 j-- 然后和 x 进行比较,直到 a[j] 找到一个小于 x 的值,则将 x 的值 7 存进 j+1 的位置。

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

  1. 插入到正确位置:a[j+1] = x

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

顺序查找法有个明显的问题,每一次查找都要检查数组下标是否越界,每一次循环都要比较两次。

直接插入排序使用 哨兵

  1. 将要插入的元素复制为哨兵:L.r[0] = L.r[i]

在这里插入图片描述

  1. 记录后移,查找插入位置。

在这里插入图片描述

  1. 插入到正确位置:L.r[j+1] = L.r[0]
    • 找到插入位置了以后,直接将哨兵上的元素插入进去。

在这里插入图片描述

每一次插入之前,先拿待插入的元素和有序序列的最后一个元素比较一下,如果大于,则不用进行比较,直接插入即可。

用32和16比较,如果大于16则直接查插在最后。
在这里插入图片描述

直接插入排序算法

//对顺序表 L 做直接插入排序
void InesrtSort(SqList &L)
{
		for(i=2;i<=L.length;i++)
		{
				if(L.r[i].key<L.r[i-1].key)		//小于,则则需将r[i]插入有序子表
				{
						L.r[0] = L.r[i];	//将待插入的记录暂时存到监视哨中
						L.r[i] = L.r[i-1];	//r[i-1]后移
						for(j=i-2;L.r[0].key<L.r[j].keylj--)	//从后向前寻找插入位置
						{
								L.r[j+1] = L.r[j];	//记录逐个后移,直到找到插入位置
						}
						L.r[j+1] = L.r[0];	//将哨兵r[0]即原来的r[i],插入到正确位置
				}
		}
}

直接插入排序性能分析

实现排序的基本操作有两个:

  1. 比较序列汇总两个关键字的大小。
  2. 移动记录(数据元素)。

最好的情况

  • 关键在在记录序列汇总顺序有序
    • 和有序序列最后一位比较的时候发现,直接比最后一位大,无须移动。

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

最坏的情况

  • 关键字在记录序列中逆序有序
    • 要插入的每个元素需要和所有的元素进行比较。

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

平均的情况

在这里插入图片描述

时间复杂度结论

  • 原始数据越接近有序,排序速度越快。
  • 最坏情况下(输入数据是逆序有序的):Tw(n) = O(n2)
  • 平均情况下,耗时差不多是最坏情况的一半:Te(n) = O(n2)
  • 要提高查找速度:
    • 减少元素的比较次数。
    • 减少元素的移动次数。

2. 折半插入排序

  • 先将第 i 个位置的元素保存到哨兵中,在被插入的区域进行折半查找,
  • 用中间值与 i 进行比较,如果 mid < i 则 high = mid-1在右半区找。
  • 如果 mid > i,则在左半区找,low = mid+1。
    • 如果最后找到正确位置,则将插入位置之后的元素往后移动,将哨兵里存的值插进去。
    • 如果找了半天,结果在哨兵中才找到想要的结果,则说明找不到了。

在这里插入图片描述

算法描述

//对顺序表L左折半插入排序
void BInsertSort(SqList &L)
{
		for(i=2;i<=L.length;i++)//依次插入第2~第n个元素
		{
				L.r[0] = L.r[i];//将待插入的元素存到哨兵位置
				low = 1;high = i-1;//设置查找区间初值
				while(low <= high)//采用二分查找法查找插入位置
				{
						mid = (low + high) / 2
						if(L.r[0].ley < Lr[mid].key)//要在左半区间找
						{
								high = mid - 1;
						}
						else //在右半区间找
						{
								low = mid + 1;
						}
				}
				//循环结束,high+1为插入位置
				for(j = i-1;j>=high+1;j--)
				{
						L.r[j+1]=L.r[j];//将插入位置之后的元素往后移动
				}
				L.r[high+1]=L.r[0];//将r[0]即原r[i],插入到正确位置
		}
}

算法分析

  • 折半查找比顺序查找块,所以折半插入排序就平均性能来书比直接插入排序更快。
  • 它所需要的关键码比较次数与待排序对象序列的初始排列无关,仅依赖于对象个数。在插入第 i 个对象时,需要经过 log₂i + 1 次关键码比较,才能确定它应该插入的位置。
    • 当 n 较大时,总关键码比较次数比直接插入排序的最坏情况要好的多,但是比其最好情况要差;
    • 在对象的初始排列已经被关键码排好序或接近有序时,直接插入排序比折半插入排序执行的关键码比较次数要少;
  • 折半插入排序的对象移动次数与直接插入排序相同,依赖于对象的初始排列。
    • 减少了比较次数,但没有减少移动次数
    • 平均性能优于直接插入排序。

在这里插入图片描述

3. 希尔排序

在这里插入图片描述

基本思想

  • 先将整个待排序记录序列分隔成若干个子序列,分别进行直接插入排序,待整个序列中的记录基本有序时,再对全体记录进行一次直接插入排序。

希尔排序特点

  1. 缩小增量。
  2. 多遍插入排序。

举个例子

  • 现有一组无序的初始数据

在这里插入图片描述

  • 在插入排序的时候选择增量为 5 ,将这样一组数据以 5 个为一组,分成若干子序列。

在这里插入图片描述

  • 在进行插入排序的时候,先让这三个元素进行比较,比较完之后交换位置。
    • 交换完位置之后,这三个元素就离他们最终的位置比较近了。

在这里插入图片描述

  • 之后就是在这 5 间隔分成的子序列中,每次都挑三个元素来进行比较和交换位置。

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

  • 在 5 间隔插入排序完成之后,每个元素都在它们自己的组内有序。
    • 这样就可以让元素一次移动的位置比较大,快速接近最终位置。

在这里插入图片描述

  • 下一次就进行 3 间隔的排序,将这个序列 3 个为一组,分成若干个子序列。
    • 让这些 3 间隔的元素再进行比较然后交换位置(蓝跟蓝比,粉跟粉比,绿跟绿比)。

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

  • 最后再做一遍 1 间隔的排序(就是直接插入排序)。
    • 此时这些数据元素已经基本有序了,再进行直接插入排序那是相当之快。

在这里插入图片描述

希尔排序思路

  1. 定义增量序列 Dk:Dm > Dm-₁ >…>D₁ = 1。
    • 刚才的例子中:m 是选择增量的次数,D₃ = 5,D₂ = 3,D₁ = 1(第一次的增量为 5,第二次为 3 …)。
  2. 对每个 Dk 进行 Dk- 间隔,插入排序(k = M,M-1,…1)。
    • 增量选择 5 的时候就进行 5 间隔插入排序,其余同理。
    • 增量为 1 的时候,进行的是所有元素的直接插入排序。

希尔排序特点

  • 一次移动,移动位置较大,跳跃式的接近排序后的最终位置。
  • 最后一次只需要少量移动。
  • 增量序列必须是递减的,最后一次必须是 1 间隔。
  • 增量序列应该是互为质数的。

希尔排序算法

主程序

//按增量序列dlta[0...t-1]对顺序表L作希尔排序
void ShellSort(SqList &L,int dlta[],int t)//dk值依次存在dlta[t]中
{
		for(k=0;k<t;k++)//规定有t趟希尔排序
		{
				ShelInsert(L,dlta[k]);//一趟增量为dlta[k]的插入排序
		}
}//ShellSort

其中某一趟的排序操作

//对顺序表L做一趟增量是dk的希尔插入排序
void ShellInsert(SqList &L,int dk)
{
		for(i=dk+1;i<=L.length;i++)
		{
				if(L.r[i].ley < L.r[i-dk].key)	//需要将L.r[i]插入有序增量子表,
											//将当前位置的元素和当前位置 - 间隔的位置的元素进行比较,
											//如5间隔:假设当前位置为6,则需要和6-5=1号位置元素进行比较
				{
						L.r[0] = L.r[i];	//暂时存在L.r[0]
						for(j = i-dk;j > 0&& L.r[0].key < L.r[j].key;j -= dk)
						{
								L.r[j+dk] = L.r[j];		//记录后移,直接找到插入位置
						}
						L.r[j+dk] = L.r[0];		//将r[0]即原r[i],插入到最终位置。
				}	
		}
}

希尔排序算法分析

希尔排序的时间效率

希尔排序算法效率与增量序列的取值有关

  • Hibbard 增量序列
    • Dk = 2k-1 —— 相邻元素互为质数。
    • 最坏情况:Tworst = O(n3/2)。
    • 猜想:Tavg = O(n5/4)。
  • Sedgewick 增量序列
    • {1,5,19,41,109…}
      • —— 9 X 4i - 9 X 2i +1 或者 4i - 3 X 2i +1
    • 猜想:Tavg = O(n7/6) ,Tworst = O(n4/3)。

希尔排序算算的稳定性

希尔排序是一种不稳定的排序算法

【例如】:现有这样一组数据,对它进行增量为 2(d=2) 的希尔排序。

在这里插入图片描述

  • 每一组都包括一个字体大的元素,和一个字体小的。
  • 排序完成之后发现,两个相同的元素 8 的相对位置变了

在这里插入图片描述

希尔排序算法分析

  • 时间复杂度是 n 和 d 的函数:O(n1.25) ~ O(1.6n1.25) — 经验公式。
  • 空间复杂度为 O(1)
  • 是一种不稳定的排序方法。
    • 如何选择最佳的增量(d) 序列,目前尚未解决
      • 最后一个增量值必须为 1,无除了 1 之外的公因子。
      • 不宜在链式存储结构上实现。

排序方法比较

在这里插入图片描述

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

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

相关文章

MQ相关概念

1) 队列管理器 队列管理器是MQ系统中最上层的一个概念&#xff0c;由它为我们提供基于队列的消息服务。 2) 消息 在MQ中&#xff0c;我们把应用程序交由MQ传输的数据定义为消息&#xff0c;我们可以定义消息的内容并对消息进行广义的理解&#xff0c;比如&#xff1a;用户的各种…

JavaWeb-FilterListener

JavaWeb-Filter&Listener 1&#xff0c;Filter 1.1 Filter概述 Filter 表示过滤器&#xff0c;是 JavaWeb 三大组件(Servlet、Filter、Listener)之一。 过滤器可以把对资源的请求拦截下来&#xff0c;从而实现一些特殊的功能。 如下图所示&#xff0c;浏览器可以访问服…

JAVA性能统计项目

一、项目背景&#xff1a;我们希望设计开发一个小的框架&#xff0c;能够获取接口调用的各种统计信息&#xff0c;比如&#xff0c;响应时间的最大值&#xff08;max&#xff09;、最小值&#xff08;min&#xff09;、平均值&#xff08;avg&#xff09;、百分位值&#xff08…

力扣OJ(2000+)

目录 2032. 至少在两个数组中出现的值 2037. 使每位学生都有座位的最少移动次数 2042. 检查句子中的数字是否递增 2097. 合法重新排列数对 2180. 统计各位数字之和为偶数的整数个数 2185. 统计包含给定前缀的字符串 2283. 判断一个数的数字计数是否等于数位的值 2287. …

基于MBD 的软件品质保证技术

基于MBD的软件是什么&#xff1f; 基于MBD的软件是基于模型开发的软件&#xff0c;主要应用于汽车、电子电气、机器人、航空、航天等行业。 ​​​ 与使用现有代码开发程序的方法不同&#xff0c;MBD 方法包括首先开发模型&#xff0c;将模型转换为代码&#xff0c;然后基于转换…

Ansys Speos | 2023R1 动态仿真助力车灯早期优化

前言 光学仿真是产品设计师应用的关键工具之一&#xff0c;能让用户在制作物理原型之前就通过数字环境体验产品。这对汽车领域来说显得尤为重要&#xff0c;随着汽车照明功能&#xff08;如转向指示灯&#xff09;越来越生动&#xff0c;TIER-1 需要能够在样件前&#xff0c;通…

Mac安装android studio

1. 下载as 下载地址 2. 安装 3. 启动软件 4.创建新项目 选择空白活动 名字为FirstApp&#xff0c;语言选择java 等待项目加载完毕 项目加载完毕 5.创建设备 6. 启动项目

【28】C语言 | 关于指针练习(1)

目录 1、下列输出什么 2、计算求和 3、使用指针打印数组内容 4、打印水仙花数 5、写一个函数&#xff0c;可以逆序一个字符串的内容 6、题目名称:打印菱形 6、喝汽水 7、猜名次 8、下列关于整形数组输出什么并解释 9、下列关于字符数组输出什么并解释 9.1 下列关于字…

【C++详解】——初识STL(string类的使用)

&#x1f4d6; 前言&#xff1a;STL(standard template libaray-标准模板库)&#xff1a;是C标准库的重要组成部分&#xff0c;不仅是一个可复用的组件库&#xff0c;而且一个包罗数据结构与算法的软件框架。 目录&#x1f552; 1. string 概述&#x1f552; 2. 标准库中的stri…

【小程序 | 黑马优选】tabBar、首页制作

文章目录tabBar制作首页制作配置网络请求制作轮播图效果渲染轮播图的UI解构配置小程序分包点击轮播图跳转到商品详情页面封装 uni.$showMsg() 方法分类导航区制作楼层区域制作tabBar制作 在 pages 目录中&#xff0c;创建首页(home)、分类(cate)、购物车(cart)、我的(my) 这 4…

windows下zookeeper搭建

程序包下载 官网下载地址 下载解压后如下&#xff01; 注意&#xff0c;zookeeper需要java环境&#xff0c;如果配置了JAVA_HOME那最好&#xff0c;如果没配置就会出现点击bin下的zkServer.cmd后CMD窗口一闪而过 修改配置 如果本地端口没有特别要求可以直接复制conf下的zo…

多臂PEG衍生物8-Arm PEG-SAA,8-Arm PEG-Succinamide Acid,八臂PEG丁二酸酰胺

一&#xff1a;产品描述 1、名称 英文&#xff1a;8-Arm PEG-SAA&#xff0c;8-Arm PEG-Succinamide Acid 中文&#xff1a;八臂-聚乙二醇-丁二酸酰胺 2、CAS编号&#xff1a;N/A 3、所属分类&#xff1a;Carboxylic acid PEG Multi-arm PEGs 4、分子量&#xff1a;可定制…

IDEA新建js项目(hello)和执行js脚本

一)、安装Node.js具体操作参考:https://blog.csdn.net/xijinno1/article/details/128774375二)、IDEA中新建js项目(hello world)1.按照下图&#xff0c;新建js项目2.选中示例代码文件后点击鼠标右键&#xff0c;选中菜单栏中的 运行* 栏目运行代码(第一次运行代码的方式)3.若是…

【版本控制】Git快速上手

Do you know what Git is&#xff1f; 一.引入 (1) 作用 Git 是一个分布式版本控制系统&#xff0c;主要是用于管理开发过程中的源代码文件&#xff08;Java类&#xff0c;xml文件&#xff0c;html页面等&#xff09;。可用于代码回溯&#xff0c;版本切换&#xff0c;多人协作…

AcWing 292. 炮兵阵地(状态压缩DP)

AcWing 292. 炮兵阵地&#xff08;状态压缩DP&#xff09;一、题目二、思路1、分析2、状态表示3、状态转移4、循环设计5、初末状态三、代码一、题目 二、思路 1、分析 这道题的话和我们之前讲解的AcWing 327. 玉米田&#xff08;状态压缩DP&#xff09;和AcWing 1064. 小国王…

Jenkins环境搭建与实战

Jenkins环境搭建与实战1、Jenkins2、GItLab的安装2.1、安装依赖2.1.1、CentOS8安装报错2.1.2、找不到对应包安装报错2.2、配置镜像2.3、安装gitlab3、安装Jenkins4、Maven安装4.1、出现报错 The JAVA_HOME environment variable is not defined correctly的错误5、Jenkins 通过…

SWIFT Framework .NET 2023

SWIFT Framework .NET 2023 Latest 2023 specification messages.Improves parsing..NET Framework 4.8 release.Performance updates.Improves handling of special characters. SWIFT Framework.NET是一个用于在组织信息系统基础架构中捕获、验证和处理SWIFT消息的系统。SWI…

3.5主存储器与CPU的连接

文章目录一、引子二、单块存储芯片与CPU的连接三、多块存储芯片与CPU的连接1.现代计算机2.命名3.增加主存的存储字长--位扩展&#xff08;1&#xff09;单块&#xff08;2&#xff09;多块4.增加主存的存储字数--字扩展&#xff08;1&#xff09;单块&#xff08;2&#xff09;…

19行列式公式和代数余子式

行列式公式 学习了关于行列式的这么多性质&#xff0c;现在我们有能力推导二阶行列式公式了&#xff1a; 观察上面的推导过程&#xff0c;不难发现&#xff0c;行列式的值等于使用性质3.b 分解后所得的那些非零行列式的和&#xff0c;所谓的非零行列式也即该行列式各行各列都…

【算法基础】大整数加减乘除法(高精度)

大整数的思想:用数组存储大整数(超长整数),比如存储1000位的整数只需要开辟一个长度为1000的数组(C++通常使用vector),今天将通过OJ例题来介绍高精度问题。(完全0基础的先建议自主学习一下,本博客默认已了解大致思想) 一、 大整数加法(大整数 + 大整数) (一)Qu…