DS八大排序之直接插入排序和希尔排序

前言

我们前面几期介绍了线性和非线性的基本数据结构。例如顺序表、链表、栈和队列、二叉树等~!本期和接下来的几期我们来详解介绍各个排序的概念、实现以及性能分析!

本期内容

排序的概念以及其运用

常见的排序算法

直接插入排序

希尔排序

一、排序的概念及其运用

排序的概念

排序:按照一定的规则,把一组元素序列以递增或递减排列起来的操作!

稳定性:假设在待排序的一组序列中有多个相同的元素,若经过排序,这些元素的相对位置(相对次序)是不变的,则称为这种排序是稳定的。否则就是不稳定的!

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

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

关于哪些是内部排序、哪些是外部排序后面性能分析会在介绍!

排序的运用

排序在日常生活中是极其常见的:例如你每天看的京东、淘宝、拼夕夕等购物平台上的综合、销量、好评、价格、等一系列都是对商品的排序!

还有高校排名:

还有大家考试的时候的排名,这些都是排序~!排序在生活只能是很常见的!!!

常见的排序算法

二、直接插入排序及其实现

插入排序的基本思想

待排序的元素序列按照大小逐个插入到已经排序好的有序序列中,直到所有待排序的元素插入完为止, 而此时得到的新的序列就是有序的序列!

这就和我们小时候玩扑克牌摸牌整理的一样,一次与前面的排比较找到合适的位置插入!

直接插入排序

第i个元素插入时(i >= 1)前面的序列已经有序的, 此时只需要用array[i]的元素与前面的所有元素逐一比较前面的元素比当前的前面的元素插入到当前位置(升序),否则当前元素插入到前面元素的后面!

我们根据上述思路写单趟改造整体

	int end = 0;//一开始end置0位置int tmp = a[end + 1];//tmp是end 的下一个位置的元素while (end >= 0){if (a[end] > tmp)//前面元素比当前的元素大{a[end + 1] = a[end];//前面元素的插入到前面元素的后一个位置}else//前面元素不比当前的元素大{a[end + 1] = tmp;//当前元素插入到前面元素的下一个位置break;//记得结束否则会又把排好的区间搞乱}--end;}if (end < 0)//所有的都比tmp大,此时end一直减会减到-1{a[0] = tmp;//此时把tmp(end的下一个位置的元素)插入到0下标位置}

整体:

其实单趟写出来,改造整体就会很容易,我们在外面在套一层循环即可!让end从i 开始,每个元素与前面的逐一比较走单趟,直至最后有序!

void InsertSort(int* a, int n)
{for (int i = 0; i < n - 1; i++){int end = i;//一开始end置0位置int tmp = a[end + 1];//tmp是end 的下一个位置的元素while (end >= 0){if (a[end] > tmp)//前面元素比当前的元素大{a[end + 1] = a[end];//前面元素的插入到前面元素的后一个位置}else//前面元素不比当前的元素大{a[end + 1] = tmp;//当前元素插入到前面元素的下一个位置break;//记得结束否则会又把排好的区间搞乱}--end;}if (end < 0)//所有的都比tmp大,此时end一直减会减到-1{a[0] = tmp;//此时把tmp(end的下一个位置的元素)插入到0下标位置}}
}

但要注意的是:外面的for循环的判断条件,i < n - 1, 也就是说i最多走到n - 2的位置即倒数第二个元素!原因是:tmp才是每次要插入的元素,而tmp = a[end +1]是end(i)的下一个位置,如果让i 到最后一个元素的位置即n-1处,那tmp = a[end+1]就会越界!!!所以i 只能到倒数第二个元素的位置!

OK, 直接插入写完了,测试一下:

这样写是没问题,但感觉稍微有点挫~!我们在当前元素不大于前面元素的时候要判断一次,是否越界也要判断一次。

我们能不能想办法给优化一下呢?答案是肯定的!我们无论是判断当前的元素不比前面的大还是越界最后插入的都是end+1的位置,所以当在单趟的循环(while)内一旦不满足当前的比前面的大则立刻跳出当前循环。到了单趟循环外有可能是break结束的,也有可能是end < 0结束的,但我们根本不需要关心他,直接插入到end+1的位置即可~!

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

这样写简洁了不少吗,也不容易出错了~!我以前学的时候老是把上面第一种的break给忘了,最后找半天(Q^Q)....所以建议大家一般写第二种~!

复杂度分析

时间复杂度:O(N^2)  ---> 单趟是O(N),最坏情况N个元素都要走一次单趟

空间复杂度:O(1)  ---> 额外使用空间的个数是常数个

当要排序的序列接近有序时性能最好O(N)~!

OK, 我们直接插入排序就介绍到这里,下面我们来介绍一下它的优化版 ----- 希尔排序!!

三、希尔排序及其实现

我们上面介绍过直接插入的时间复杂度是O(N^2),它的性能一般~!有一天一位叫D.L.Shell的大佬去学习了直接插入排序后,想既然你直接插入排序在接近有序的情况下性能很好(O(N)),那我能不能把一组无序的元素经过处理先让他变得接近有序然后再来一趟直接插入呢?其实他这种想法直接把直接插入排序优化到几乎能和快排平起平坐了~!就是下面这位大佬:

OK,我们来看看大佬的具体思路!

希尔排序的思路

1、进行多组预排序

2、最后再来一次直接插入

什么意思呢?我来解释一下:这里的多组预排是:

先选定一个整数增量gap(一开始gap = n / 2),将数组的元素分为gap 组,将每个距离为gap的分为一组,并对每个小组进行距离为gap的"直接插排",然后不断的缩小gap(gap /= 2),重复上述操作,直到gap == 1时就说明各个组的都已经排好,此时相较于一开始已经是非常有序的了~!最后我们再来一趟直接插入排序则该序列就是有序的了!

OK, 画个图理解一下:

这就将一个完整的数组分成了gap 组,此时的gap 是 5,我们走一下预排序的一个gap:

他这样走下去,gap最后一定会到gap == 1,当gap == 1那就是直接插排了:

上述栗子可以清楚的看到当gap == 1时,未开始最后一次直接插排前的数组已经是很有序了,当经过最后一趟直接插入排序后就是有序了~!

希尔排序的实现

还是先单趟,再整体~!

单趟

每一组的单趟

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

注意的是这里是n - gap 而不是n - gap - 1

这是一组的单趟,这里和直接插入排序几乎一样,当gap == 1就是直接插排~!此时有多少组,就走多少组这样的单趟,所以在外面在套一层循环才是所有组的单趟~!

所有组的单趟

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

这就是所有的预排序的单趟,那他到底走多少趟预排呢?具体不知道,当gap最后通过调整到(gap /= 2或gap = gap / 3 + 1) gap == 1即可!所以总体应该在外面套个循环:

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

gap 一开始是n,然后开始执行第一趟时时gap /= 2;然后每次调整都是/=2,最后一次进入循环一定是1(n, n / 2, n / 4, n / 8, n / 16, n /32 ..... 4, 2, 1),即直接插入排序了~!

OK,测试一下:

没问题!但这段代码被另一位大佬进行过优化,如下:

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

他这里把,单组逐一排改成了多组并排~~以前我们是一组跑完了再去排另一组,他改完后直接一遍就可以把多组排好~!但性能上没有差别,,。

这里他给的gap = gap / 3 + 1;这样写的效率其实比gap /= 2的要好一点(网上以前看到的),他这里+1是因为,gap / 3有时候会小于1(例如: 2 / 3),这样最后一趟就排不了了~!+1就可以解决这个问题!

复杂度分析

希尔排序的时间复杂度是极其不好计算的,因为它的gap 的取值方法太多,很难精确地去计算,在好多书中给的都不同:例如严蔚敏老师的书中是如下

殷人昆老师的书中如下:

由于这里我们的gap是按殷人昆老师提出的方式取的,而殷人昆老师也对其进行了大量的数据实验统计,我们可以暂时认为

希尔排序的时间复杂度为:O(N^1.25)或O(N^1.3)

希尔排序的空间复O(1)

OK,本期插入排序就先介绍到这里,好兄弟,我们下期选择排序见~!

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

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

相关文章

Zabbix 6 详细安装部署教程

目录 一、安装 MySQL 数据库 二、安装 zabbix 监控平台 三、编辑配置文件 四、启动服务 五、zabbix-web 安装 zabbix web 出图展示乱码问题解决方案 zabbix 的安装部署非常简单&#xff0c;官方提供了四种安装途径&#xff0c;分别是二进制 rpm 包安装方式、源码安装方…

jetson nano 串口通信

目录 1.UART通信介绍 2.电脑端准备工作 2.1 安装串口调试助手 2.2 硬件接线 3.Jetson Nano端准备工作 3.1安装库文件 3.2修改主板上电启动串口权限 4.示例程序-发送及接收 4.1 开启串口调试助手 4.2 导入示例程序 4.3 执行程序 4.4 查看效果 4.4.1 串口调试端 4.4…

西南科技大学信号与系统A实验三(线性连续时间系统的分析)

一、实验目的 1.掌握用 matlab 分析系统时间响应的方法 2.掌握用 matlab 分析系统频率响应的方法 3.掌握系统零、极点分布与系统稳定性关系 二、实验原理 1. 系统函数 H(s) 系统函数:系统零状态响应的拉氏变换与激励的拉氏变换之比. H(s)=R(s)/E(s) 在 matlab 中可采用…

ddns-go部署在linux虚拟机

ddns-go部署ubuntu1804 1.二进制部署 1.虚拟机部署 1.下载linux的x86二进制包 wget https://github.com/jeessy2/ddns-go/releases/download/v5.6.3/ddns-go_5.6.3_linux_x86_64.tar.gz2.解压 tar -xzf ddns-go_5.6.3_linux_x86_64.tar.gz3.拷贝执行文件到PATH下&#xff0c…

【前端】浅谈async/await异步传染性

文章目录 概述观点无法解决可以解决 来源 概述 "异步传染性"问题通常是指&#xff0c;当一个函数使用了async和await&#xff0c;其调用者也需要使用async和await处理异步操作&#xff0c;导致整个调用链都变成异步的。这种情况可能导致代码变得更复杂&#xff0c;不…

全网日志智能聚合及问题根因分析

1 日志关联分析的挑战 随着各行各业数字化转型的不断深入&#xff0c;网络承载了人们日常生活所需的政务、金融、娱乐等多方面的业务系统&#xff0c;已经成为影响社会稳定运行、关系国计民生的重要基础设施资源。哪怕网络发生及其微小的故障&#xff0c;也可能带来难以估量的…

基于C#实现十字链表

上一篇我们看了矩阵的顺序存储&#xff0c;这篇我们再看看一种链式存储方法“十字链表”&#xff0c;当然目的都是一样&#xff0c;压缩空间。 一、概念 既然要用链表节点来模拟矩阵中的非零元素&#xff0c;肯定需要如下 5 个元素(row,col,val,down,right)&#xff0c;其中&…

C语言第三十六弹--实现转移表的多种方法

使用C语言通过多种方法实现转移表 方法一、普通法 思路&#xff1a;如图实现多种操作&#xff0c;首先创建菜单&#xff0c;需要运行一次再判断条件&#xff0c;所以通过do{}while(); 循环来实现多次。有多种选择&#xff0c;使用switch case选择语句&#xff0c;再在对应case…

SpectralGPT: Spectral Foundation Model 论文翻译2

遥感领域的通用大模型 2023.11.13在CVPR发表 原文地址&#xff1a;[2311.07113] SpectralGPT: Spectral Foundation Model (arxiv.org) 实验 ​ 在本节中&#xff0c;我们将严格评估我们的SpectralGPT模型的性能&#xff0c;并对其进行基准测试SOTA基础模型&#xff1a;ResN…

【沁恒蓝牙mesh】CH58x 将RTC时钟切换为LSE外部低速时钟

本文主要记录了【沁恒蓝牙mesh】CH58x 如何将RTC时钟切换为外部时钟 &#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是喜欢记录零碎知识点的小菜鸟。&#x1f60e;&#x1f4dd; 个人主页&#xff1a;欢迎访问我的 Ethernet_Comm 博客主页&#x1f525;&#x1f389;…

【代码】基于卷积神经网络(CNN)-支持向量机(SVM)的分类预测算法

程序名称&#xff1a;基于卷积神经网络&#xff08;CNN&#xff09;-支持向量机&#xff08;SVM&#xff09;的分类预测算法 实现平台&#xff1a;matlab 代码简介&#xff1a;CNN-SVM是一种常用的图像分类方法&#xff0c;结合了卷积神经网络&#xff08;CNN&#xff09;和支…

Roll-A-Ball 游戏

Roll-A-Ball 游戏 1&#xff09;学习资料 b站视频教程&#xff1a;https://www.bilibili.com/video/BV18W411671S/文档&#xff1a; * Roll-A-Ball 教程&#xff08;一)&#xff0c; * Roll-A-Ball 教程&#xff08;二)线上体验roll-a-ball成品 * http://www-personal.umich.e…

Databend 开源周报第 121 期

Databend 是一款现代云数仓。专为弹性和高效设计&#xff0c;为您的大规模分析需求保驾护航。自由且开源。即刻体验云服务&#xff1a;https://app.databend.cn 。 Whats On In Databend 探索 Databend 本周新进展&#xff0c;遇到更贴近你心意的 Databend 。 支持追加流 Da…

技巧-PyTorch中num_works的作用和实验测试

简介 在 PyTorch 中&#xff0c;num_workers 是 DataLoader 中的一个参数&#xff0c;用于控制数据加载的并发线程数。它允许您在数据加载过程中使用多个线程&#xff0c;以提高数据加载的效率。 具体来说&#xff0c;num_workers 参数指定了 DataLoader 在加载数据时将创建的…

网络安全--基于Kali的网络扫描基础技术

文章目录 1. 标准ICMP扫描1.1使用Ping命令1.1.1格式1.1.2实战 1.2使用Nmap工具1.2.1格式1.2.2实战1.2.2.1主机在线1.2.2.2主机不在线 1.3使用Fping命令1.3.1格式1.3.2实战 2. 时间戳查询扫描2.1格式2.2实战 3. 地址掩码查询扫描3.1格式3.2实战 2. TCP扫描2.1TCP工作机制2.2TCP …

基于YOLO模型建筑工地个人防护设备目标检测

使用安全装备可以保护他们免受建筑工地的意外事故。据统计&#xff0c;每年有数以万计的工人在建筑工地受到严重伤害&#xff0c;造成终生困难。然而&#xff0c;通过自我监控来确保工人穿戴个人防护装备非常重要。在这方面&#xff0c;需要一个准确和快速的系统来检测工人是否…

鸿蒙开发-ArkTS 语言-状态管理

[写在前面: 文章多处用到gif动图&#xff0c;如未自动播放&#xff0c;请点击图片] 衔接上一篇:鸿蒙开发-ArkTS 语言-基础语法 3. 状态管理 变量必须被装饰器装饰才能成为状态变量&#xff0c;状态变量的改变才能导致 UI 界面重新渲染 概念描述状态变量被状态装饰器装饰的变…

ArrayList源码全面解析

一、概述 ArrayList 是 java 集合框架中比较常用的数据结构,继承自 AbstractList&#xff0c;实现了 List 接口。底层采用数组来实现。ArrayList 实现了java.io.Serializable接口&#xff0c;这意味着ArrayList支持序列化&#xff0c;能通过序列化去传输。 1.1、底层数据结构…

python基础练习题库实验6

文章目录 题目1代码实验结果题目2代码实验结果题目3代码实验结果题目4代码实验结果题目总结题目1 根据以下规范编写一个函数: 函数名称:triple输入参数:1个输入参数数据类型字符串返回值:函数返回1个字符串值。该字符串由每个字符重复3次的句子构成。例如,如果句子是Uni,…

Vue2问题:如何全局使用less和sass变量?

前端功能问题系列文章&#xff0c;点击上方合集↑ 序言 大家好&#xff0c;我是大澈&#xff01; 本文约2400字&#xff0c;整篇阅读大约需要4分钟。 本文主要内容分三部分&#xff0c;如果您只需要解决问题&#xff0c;请阅读第一、二部分即可。如果您有更多时间&#xff…