数据结构——排序(1):插入排序

目录

一、排序的概念

二、排列的运用

三、常见的排序算法

四、插入排序

1.直接插入排序

 (1)思路

(2)过程图示

(3)代码实现

 (4)代码解释

(5)特性

2.希尔排序

(1)思路

(2)过程图示

(3)代码实现

(4)代码解释

(5)特性 

(6)时间复杂度

 五、写在最后


一、排序的概念

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

二、排列的运用

(1)购物筛选排序

(2)院校排名排序

三、常见的排序算法

四、插入排序

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

联想我们在实际中玩的扑克牌,就用到了插入排序的思想~

1.直接插入排序

 (1)思路

插入到第i个元素时,其前面的元素已经排好序了,此时将第i个元素的大小与第i-1、i-2……个元素的大小进行比较,找到合适的位置,将原来位置上的元素后移,并将第i个元素插入。


(2)过程图示

我们以数组{2,1,3,5,4,8}为例:

①首先end指的是已排好序的最后一个元素;tmp为end之后的元素,即要比较插入的元素。

如图:tmp比end小,那么应该将tmp往前遍历。让end+1位置的数据为end位置的数据,end往前遍历,此时end<0无法再遍历,则跳出循环。

②③继续遍历:随后的两次中tmp均大于end对应的数据,直接进行下一此遍历。

④此时tmp小于end对应的位置,令end+1位置的数据为end位置的数据,end后移继续遍历。此时tmp大于end对应位置的数据,那么令此时end+1位置的数据为tmp。

⑤继续遍历,此时tmp大于end,直接进行下一次遍历。

⑥继续遍历我们发现end指向最后一个数据时,tmp越界了,说明该情况不合理。我们也可以知道在写代码时,end的约束条件应该是<n-1。

至此遍历完成,所有数据也排为了升序。


(3)代码实现

void InsertSort(int* arr, int n)
{for(int i = 0 ; i < n - 1; i ++){    int end = i;int tmp = arr[end + 1];while(end >= 0){if(tmp < arr[end]){arr[end+1] = arr[end];end--;}else {    break;}}arr[end+1] = tmp;}
}

 (4)代码解释

①首先最外层的for循环,用i遍历下标为0 ~ n-2的数据,即最后一个数据没有被遍历(前面有解释,end = i ,且end不能为最后一个数据,因为tmp会越界);

②end为已经排好序的范围的最后一个数据;tmp为被比较之后插入的元素;

③while循环用来限制end不能出界,即end<0或者tmp>=arr[end](else情况),都会跳出循环执行下一步:令end+1位置的数据为tmp储存的数据;

④如果tmp比end指向的数据小,即tmp应该排在end前面,就令end+1的位置数据为end对应的数据,相当于给tmp腾位置,遍历寻找一个end<tmp且end+1>tmp的位置,插入tmp。


(5)特性

①元素集合越接近有序,算法的时间效率越高;

②时间复杂度:O(n^2):最差的情况是序列为降序(要排列为升序),即Tmp要与其前面的每个数据都比较。O(2+3+……+n-1+n) = O(n^2);

③空间复杂度:O(1)。


2.希尔排序

我们学习了直接插入排序,但是它的时间复杂度为O(n^2),效果并不是很理想。而这个结果是基于降序的情况计算的,那么可以思考一下,我们是不是能够将其优化一下,避免这种的情况出现呢?

(1)思路

希尔排序法又称缩小增量法。

先选定一个整数(定义为gap),把待排序的文件分成若干组,所有的距离相等的数据分在同一组,并对每一组的数据进行排序,然后gap = gap / 3 + 1得到下一个新的gap,再将数据进行分组并对每组进行插入排序,在这个过程中gap会越来越小,当gap等于1时就相当于直接插入排序算法。(那么,为什么新的gap要等于gap/3+1呢?这是因为相较于除以2、除以4来说,除以3所分的组数不是太多,也不会太少;并且在最后+1避免了达不到gap=1的情况。)


(2)过程图示

我们以数组{9,1,2,5,7,4,8,6,3,5}、gap等于5为例:

相同颜色的数据为一组,由于gap等于5,我们将以上10个数据分为了5组,每组2个数据。将每组的两个数据进行比较(此时靠前的数据为end,靠后的数据为tmp),如果tmp小于end对应的数据,则让end+gap对应的数据为end所对应的数据,end往前遍历(end-gap)。巧的是,在这几组中end-gap均小于0,跳出循环。然后继续遍历…… 就这样,我们将每组数据进行了排序,得到{4,1,2,3,5,9,8,6,5,7};

gap改变,gap等于2、1的情况亦是如此:

得到{2,1,4,3,5,6,5,7,8,9};

得到{1,2,3,4,5,5,6,7,8,9},至此排序完成。 


(3)代码实现

我们可以根据将每组进行排序的思路写代码:

void ShellSort(int* arr, int n)
{int gap = 3;for(int i = 0 ; i < gap ; i ++){for(int i = 0 ;i < n - gap; i += gap){int end = i;int tmp = arr[end + gap];while(end >= 0){    if(tmp < arr[end]){arr[end + gap] = arr[end];end -= gap;}else {break;}}arr[end + gap] = tmp;}}
}

第一个for循环是将分成的3组进行排序,第二个for循环及以下是将每组数据进行排序,只是在直接插入排序的基础上的优化。可是,现在怎么比之前还要多一个循环呢?可想而知时间复杂度并不理想,我们应该设法去掉这个for循环: 

void ShellSort(int* arr, int n)
{int gap = n;while(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){if(tmp < arr[end]){    arr[end + gap] = arr[end];end -= gap;}else{break;}}arr[end + gap] = tmp;}    }
}

区别在于前者是每一组内部的数据先进行比较(以组为单位),后者为按照原有的顺序进行遍历,当然所插入的数据是与其所在组中的数据进行比较。


(4)代码解释

其实与直接插入排列类似:

①gap的限制条件为gap>1,gap不能等于1(如果等于1,gap=gap/3+1的计算中gap一直为1,会造成死循环);

②最外层的for循环,用i遍历下标为0 ~ n-gap-1的数据,即end-gap位置的数据没有被遍历。解释同上,end = i ,且end不能为最后一个数据,因为tmp会越界(若end=n-gap,那么tmp=n-gap+gap=n,此时越界);

③end为已经排好序的范围的最后一个数据;tmp为被比较之后插入的元素;

④while循环用来限制end不能出界,即end<0或者tmp>=arr[end](else情况),都会跳出循环执行下一步:令end+gap位置的数据为tmp储存的数据;

⑤如果tmp比end指向的数据小,即tmp应该排在end前面,就令end+gap的位置数据为end对应的数据,相当于给tmp腾位置,遍历寻找一个end<tmp且end+gap>tmp的位置,插入tmp。


(5)特性 

①希尔排序是对直接插入排序的优化;

②gap>1都是预排序,目的是让数组更接近有序;当gap等于1时,数组已经接近有序,这样排序的效率更高。


(6)时间复杂度

外层循环的时间复杂度为O(log2 N)或者O(log3 N),即为O(log N);

内存循环的时间复杂度为O(n^1.3):

假设有n个数据,距离为gap,那么一共有gap组,每组有n/gap个数据。

最坏的情况下:

每组的移动次数为1+2+3+……+(n/gap - 1),一共有gap组,因此总移动总数为gap*[ 1+2+3+……+(n/gap - 1)]。

当gap为n/3时,移动总次数为:n/3 *(1 +2 ) = n;

当gap为n/9时,移动总次数为:n/9 *(1 +2 + …… + 8) = n/9 * 8(1+8)/2 = 4n;

最后一趟,gap等于1时,即为直接插入排序,内层循环排序消耗为n。

根据上述分析,可画出这样的曲线图:

因此,希尔排序在最初和最后的排序的次数都为n,即前一阶段排序次数是逐渐上升的状态,当到达某⼀顶点时,排序次数逐渐下降至n,而该顶点的计算暂时无法给出具体的计算过程。

希尔排序时间复杂度不好计算,因为 gap 的取值很多,导致很难去计算,因此很多书中给出的希尔排序的时间复杂度都不固定。

《数据结构(C语言版)》---严蔚敏书中给出的时间复杂度为:


 五、写在最后

这节课我们学习了插入排序,而排序好几种。

敬请期待“选择排序”~

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

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

相关文章

CSS技巧专栏:一日一例 20-纯CSS实现点击会凹陷的按钮

本例图片 案例分析 其实这个按钮非常的简单啊,主要就是利用了box-shadow的inset。 布局代码 <button class="base">凹下的按钮</button> 基础样式 :root{--main-bg-color: #dcdcdc; /* 将页面背景色调整为浅灰色 */--color:#000;--hover-color:#99…

LLM - GQA 之 Group Query Attention 论文与源码精读

目录 Abstract 1.Introduction 2.Method 2.1 UpTraining 2.2 Grouped-query attention 3.Experiments 3.1 Experimental setup 3.2 Main Results 3.3 Ablations 4.Related Work 5.Conclustion 6.Code 6.1 repeat_kv 6.2 Attention Arg Init 6.3 Attention Mat I…

IT服务质量管理攻略(至简)

质量管理、风险管理和信息安全管理是IT服务监督管理的重要内容&#xff0c;三者之间相对独立。IT服务质量管理是通过制订质量方针、质量目标和质量计划&#xff0c;实施质量控制、质量保证和质量改进活动&#xff0c;确保IT服务满足服务级别协议的要求&#xff0c;最终获得用户…

openvidu私有化部署

openvidu私有化部署 简介 OpenVidu 是一个允许您实施实时应用程序的平台。您可以从头开始构建全新的 OpenVidu 应用程序&#xff0c;但将 OpenVidu 集成到您现有的应用程序中也非常容易。 OpenVidu 基于 WebRTC 技术&#xff0c;允许开发您可以想象的任何类型的用例&#xf…

[算法]第一集 递归(未完待续)

递归啊递归&#xff0c;说简单简单&#xff0c;说难难。 首先我们要知道 一、什么是递归&#xff1f; 我们再C语言和数据结构里都用了不少递归&#xff0c;这里就不多详细介绍。 递归简单来说就是函数自己调用自己的情况 二、为什么要用递归呢&#xff1f; 本质来说其实就…

【WRF安装第四期(Ubuntu)】搭建WRF编译所需系统-WRF和WPS模型的安装

WRF安装第四期&#xff1a;搭建WRF编译所需系统-WRF和WPS模型的安装 1 WRF的编译安装&#xff08;Building WRF&#xff09;1.1 进入Build_WRF文件夹1.2 下载WRFV4.01.3 解压WRF安装包1.4 安装WRF选择#1&#xff1a;32选择#2&#xff1a;33选择#3&#xff1a;34 1.5 检查WRF是否…

Linux 中的信号处理

Linux 中的信号处理是操作系统中非常重要的一个概念&#xff0c;通过信号处理&#xff0c;进程之间可以进行通信、协调以及实现一些重要的功能。本文将从信号的概念、类型、生成、传递、处理、以及常见的信号处理函数等方面展开讨论&#xff0c;以帮助读者更深入地了解 Linux 中…

【机器学习】BP神经网络中的链式法则

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 BP神经网络中的链式法则1. 引言2. 链式法则基础2.1 什么是链式法则&#xff1f;…

29.Labview界面设计(下篇) --- 自定义控件库、界面布局与外观设计

摘要&#xff1a; 题主在上一篇文章中向大家讲解了前面板逻辑框架及结构的搭建和控件的类型介绍&#xff0c;那么本章主要围绕前面板的控件布局以及控件的自定义类型和背景等外观优化项中来讲解。 本篇文章讲解界面设计的下篇内容&#xff0c;上篇内容链接大家可以直接点击链接…

国家统计局中国主要城市面板数据(1990-2023年)

数据说明&#xff1a;数据来源于国家统计局&#xff0c;指标包含&#xff1a;城市、年份、第三产业增加值、第一产业增加值 地区生产总值、第二产业增加值、年末户籍人口、城镇非私营单位在岗职工平均工资 房地产开发投资额、房地产开发住宅投资额、房地产开发办公楼投资额、房…

Linux C 程序 【03】线程栈空间

1.开发背景 上一个篇章创建了线程&#xff0c;参考 FreeRTOS&#xff0c;每个线程都是有自己的内存空间&#xff0c;Linux上面也是一样的&#xff0c;这个篇章主要描述线程栈空间的设置。 2.开发需求 设计实验&#xff1a; 1&#xff09;创建线程&#xff0c;并配置线程内存大…

培训第二十二天(mysql数据库主从搭建)

上午 1、为mysql添加开机启动chkconfig [rootmysql1 ~]# chkconfig --list //列出系统服务在不同运行级别下的启动状态注&#xff1a;该输出结果只显示 SysV 服务&#xff0c;并不包含原生 systemd 服务。SysV 配置数据可能被原生 systemd 配置覆盖。 要列出 systemd 服务…

IEEE报告解读:存储技术发展趋势分析

1.引言 随着数据科学、物联网&#xff08;IoT&#xff09;和永久存储需求的快速增长&#xff0c;对大规模数据存储的需求正在迅速增加。存储技术的发展趋势直接关系到数据的可靠性和经济性。本文将根据IEEE最新发布的《2023年国际器件与系统路线图》&#xff0c;深入探讨各种存…

AnyGPT: Unified Multimodal LLM with Discrete Sequence Modeling

发表时间&#xff1a;arXiv 2024年2月26日 论文链接&#xff1a;https://arxiv.org/pdf/2402.12226 作者单位&#xff1a; Fudan University Motivation&#xff1a; LLM 在理解和生成人类语言方面表现出非凡的能力。但是&#xff0c;LLM 的能力仅限于针对文本的处理。而现…

详解Xilinx FPGA高速串行收发器GTX/GTP(2)--什么是GTX?

文章总目录点这里:《FPGA接口与协议》专栏的说明与导航 GTX本质上是基于SerDes技术的高速串行收发器,它是FPGA内部的底层电路,也叫做Gigabit Transceiver(吉比特收发器,简称为GT)。其中A7系列使用的GT叫GTP,K7系列使用的GT叫GTX,V7系列使用的GT叫GTH和GTZ,它们…

循环神经网络和自然语言处理一

目录 一.分词 1.分词工具 2.分词的方法 3.N-gram表示方法 二.向量化 1.one-hot编码 2.word embedding 3.word embedding API 4.数据形状改变 既然是自然语言&#xff0c;那么就有字&#xff0c;词&#xff0c;句了 一.分词 1.分词工具 tokenization&#xff0c;jie…

【数据结构】二叉搜索树(Java + 链表实现)

Hi~&#xff01;这里是奋斗的明志&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f331;&#x1f331;个人主页&#xff1a;奋斗的明志 &#x1f331;&#x1f331;所属专栏&#xff1a;数据结构、LeetCode专栏 &#x1f4da;本系…

【DOCKER】显示带UI的软件

1. Linux 1.1 宿主机开放X server权限 xhost 1.2 启动容器 docker run -it --rm --privilegedtrue --useru20 --workdir/home/u20 \ -e DISPLAYhost.docker.internal:0 u20:dev1.3 测试 # 安装测试软件 sudo apt-get -y install x11-apps# 显示测试程序 xclock2. Windows …

LearnOpenGL-光照章节学习笔记

LearnOpenGL-光照章节学习笔记 颜色创建一个光照场景 基础光照一、环境光照二、漫反射光照三、镜面反射 材质光照贴图一、漫反射贴图二、镜面光贴图三、放射光贴图 投光物一、平行光二、点光源衰减实现 三、聚光灯平滑边缘 多光源一、平行光&#xff08;定向光&#xff09;二、…

免费代理池是什么,如何使用代理IP进行网络爬虫?

互联网是一个庞大的数据集合体&#xff0c;网络信息资源丰富且繁杂&#xff0c;想要从中找到自己需要的信息要花费较多的时间。为了解决这个问题&#xff0c;网络爬虫技术应运而生&#xff0c;它的主要作用就是在海量的互联网信息中进行爬取&#xff0c;抓取有效信息并存储。然…