[排序算法]插入排序+希尔排序全梳理!

目录

  • 1.排序是什么?
    • 1.1排序的概念
    • 1.2排序运用
    • 1.3常见的排序算法
  • 2.插入排序分类
  • 3.直接插入排序
    • 基本思想
    • 具体步骤:
    • 动图演示
    • 代码实现
    • 直接插入排序的特性总结:
  • 4. 希尔排序
    • 基本思想
    • 具体步骤
    • 动图演示
    • 代码实现
    • 希尔排序的特性总结:
  • 5.总结

1.排序是什么?

1.1排序的概念

排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。
稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i] = r[j],且 r[i] 在 r[j] 之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。
内部排序:数据元素全部放在内存中的排序。
外部排序:数据元素太多不能同时放在内存中,根据排序过程的要求不断地在内外存之间移动数据的排序。

1.2排序运用

这里给大家举几个生活中,常见排序的例子:

购物平台里面按某个商品的维度排序:
在这里插入图片描述
报考志愿时全国高校的排名情况:
在这里插入图片描述

其实在我们日常生活中会经常使用排序,可见排序与我们生活息息相关。

1.3常见的排序算法

在这里插入图片描述

2.插入排序分类

插入排序可以分为:直接插入排序希尔排序
在这里插入图片描述

3.直接插入排序

基本思想

直接插入排序的思路和打扑克牌时给牌排序的思路类似:

比如比如我手中有红桃 6,7,9,10 这 4 张牌,已经处于升序排列:
在这里插入图片描述
这时候,我又抓到一张黑桃 8,如何让手中的 5 张牌重新变成升序呢?

在这里插入图片描述
很简单,其实是在已经有序的 4 张牌中找到红桃 8 应该插入的位置,也就是 7 和 9 之间,把红桃 8 插进去:

在这里插入图片描述

就像玩牌一样,插入排序算法也采用了类似的思想:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列 。

具体步骤:

1)维护一个有序区,把元素一个个插入有序区的适当位置,直到所有元素都有序为止。
2)在待排序的元素中,假设前 n-1 个元素已有序,现将第 n 个元素插入到前面已经排好的序列中,使得前 n 个元素有序。按照此法对所有元素进行插入,直到整个序列有序。

接下来演示一下直接插入排序在数组中的具体实现步骤。

给定一组无序数组如下:

在这里插入图片描述
我们把首元素 6 作为有序区,此时有序区只有这一个元素:
在这里插入图片描述

第一轮,让元素 9 和有序区的元素依次比较,9 > 6,所以元素 9 和元素 6 无需交换。

此时有序区的元素增加到两个:

在这里插入图片描述

第二轮,让元素 7 和有序区的元素依次比较,7 < 9,所以把元素 7 和元素 9 进行交换:
在这里插入图片描述
7 > 6,所以把元素 7 和元素 6 无需交换。

此时有序区的元素增加到三个:

在这里插入图片描述

第三轮,让元素 4 和有序区的元素依次比较,4 < 9,所以把元素 4 和元素 9 进行交换:
在这里插入图片描述
4 < 7,所以把元素 4 和元素 7 进行交换:
在这里插入图片描述

4 < 6,所以把元素 4 和元素 6 进行交换:
在这里插入图片描述

此时有序区的元素增加到四个:
在这里插入图片描述

以此类推,插入排序一共会进行(数组长度-1)轮,每一轮的结果如下:
在这里插入图片描述

动图演示

我们来看一组动图演示:
在这里插入图片描述

代码实现

void InsertSort(int* a, int n) {
	//数组的长度是n,那么最后一个数据是n-1,倒数第二个数据是n-2
	for (int i = 0; i < n - 1; ++i) {
		// [0 end]有序,把end+1的位置的值插入进去,保持它依旧有序
		int end = i; //记录有序序列的最后一个元素的下标
		int tmp = a[end + 1]; //待插入的元素
		while (end >= 0) {
			if (tmp < a[end]) {
				a[end + 1] = a[end];
				--end;
			}
			else {
				break;
			}
		}
		//代码执行到此位置有两种情况:
		//1.待插入元素找到应插入位置(break跳出循环到此)。
		//2.待插入元素比当前有序序列中的所有元素都小(while循环结束后到此)。
		a[end + 1] = tmp;
	}
}

直接插入排序的特性总结:

  1. 元素集合越接近有序,直接插入排序算法的时间效率越高
  2. 时间复杂度:O(N^2)

最好的情况:数组是有序的或者接近有序的,那么时间复杂度就是:O(N)
最坏的情况:数组是逆序的,那么时间复杂度就是: O(N^2)
元素集合越接近有序,直接插入排序算法的时间效率越高。

  1. 空间复杂度:O(1),这里没有额外开辟空间.
  2. 稳定性:稳定。 直接插入排序在遇到相同的数时,可以就放在这个数的后面,就可以保持稳定性了,所以说这个排序是稳定的。

4. 希尔排序

基本思想

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

希尔排序(Shell Sort)是插入排序的一种,它是针对直接插入排序算法的改进。

它的基本思想是:先选定一个整数,把待排序文件中所有记录分成个组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后,取,重复上述分组和排序的工作。当到达=1时,所有记录在统一组内排好序。

具体步骤

具体步骤是:

1)先选定一个小于 N 的整数 gap 作为第一增量,然后将所有距离为 gap 的元素分在同一组,并对每一组的元素进行直接插入排序。然后再取一个比第一增量小的整数作为第二增量,重复上述操作。
2)当增量的大小减到1时,就相当于整个序列被分到一组,进行一次直接插入排序,排序完成。

为什么要让 gap 由大到小呢?

原因是:

gap 越大,数据挪动得越快。

gap 越小,数据挪动得越慢。

前期让 gap 较大,可以让大的数据可以更快到最后,小的数可以更快到前面,减少挪动次数。

一般情况下,取序列的一半作为增量,然后依次减半,直到增量为 1

给定一组无序数组如下:

在这里插入图片描述
第一轮,我们用序列长度的一半作为第一次排序时 gap 的值,此时相隔距离为 5 的元素被分为一组(共分了 5 组,每组有 2 个元素),然后分别对每一组进行直接插入排序。

在这里插入图片描述

第二轮,gap 的值折半,此时相隔距离为 2 的元素被分为一组(共分了 2 组,每组有 5 个元素),然后再分别对每一组进行直接插入排序。
在这里插入图片描述

第三轮,gap 的值再次减半,此时 gap 减为 1,即整个序列被分为一组,进行一次直接插入排序。
在这里插入图片描述

动图演示

我们来看一组动图演示:
在这里插入图片描述

代码实现

/*希尔排序
* 时间复杂度:O(N)
* 如果gap越小,越接近有序;
* gap越大,那么大的数据可以更快到最后,小的数可以更快到前面,但它不接近有序
*/
void ShellSort(int* a, int n) {
	//1. gap>1 预排序
	//2. gap == 1 直接插入排序
	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 (tmp < a[end]) {
					a[end + gap] = a[end];
					end -= gap;
				}
				else {
					break;
				}
			}
			a[end + gap] = tmp;
		}
	}
}

希尔排序的特性总结:

  1. 希尔排序是对直接插入排序的优化。

  2. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就
    会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。

  3. 希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算,因此在好些树中给出的
    希尔排序的时间复杂度都不固定:
    《数据结构(C语言版)》— 严蔚敏
    在这里插入图片描述

《数据结构-用面相对象方法与C++描述》— 殷人昆
在这里插入图片描述
因为我们的 gap 是按照 Knuth 提出的方式取值的,而且 Knuth 进行了大量的试验统计,我们暂时就按照:O(n1.25)到
O ( 1.6 ∗ n 1.25 ) 来计算。

时间复杂度:(N*logN),平均时间复杂度:O ( n 1.3)

空间复杂度:O ( 1 )

5.总结

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

阿里云CDN流量被盗刷或CC攻击会怎么样?

最近&#xff0c;一位使用了阿里云CDN的站长向主机吧反应&#xff0c;其域名使用的阿里云CDN不知道是因为被盗刷还是被CC攻击&#xff0c;导致不仅原本帐号上的3T流量包用完了&#xff0c;连帐户也欠了几百元的流量费。 而产生这么多流量的只是晚上睡一觉起来&#xff0c;手机…

全志H616 通过Cedrus和v4l2_request API实现硬件编解码加速(香橙派zero2)

编译安装或加载cedrus驱动模块&#xff0c;加载v4l2-mem2mem Sunxi-Cedrus 致力于为全志 SoC 提供硬件加速的视频解码和编码支持&#xff0c;并将其引入主线 Linux 内核。此外&#xff0c;还为典型的基于 GNU/Linux 的系统提供了与内核驱动程序接口的其他用户空间组件。 Sunx…

调节效应多元统计回归

什么是调节效应&#xff0c;给个例子说明一下: 背景 假设我们有一个国家的经济数据&#xff0c;我们希望研究产业数字化是否调节了环境规制对产业结构调整的影响。 步骤 1. 假设检验 原假设 (H0)&#xff1a; 产业数字化对环境规制与产业结构调整之间的关系没有调节作用。…

浏览器提示413 Request Entity Too Large

1 问题 2 解决 2.1 后端java配置 2.2 Nginx配置

【Git篇 二】idea中使用git合并分支(拉取分支)

idea中使用git合并分支 前言idea使用git合并分支1) 将主分支&#xff08;master&#xff09;更新到自己的分支&#xff08;dev&#xff09;① checkout到自己分支② 目标分支&#xff08;dev&#xff09;更新到当前分支&#xff08;dev_KC240524&#xff09;③ 当前分支出现“绿…

提升B端图表设计技能:教程分享

图表是数据可视化的常用表现形式&#xff0c;是对数据的二次加工&#xff0c;可以帮助我们理解数据、洞悉数据背后的真相&#xff0c;让我们更好地适应这个数据驱动的世界。本期就来带大家学习图表的设计及构成&#xff0c;帮助大家更好的理解图表设计。 设计教程源文件http:/…

Keras深度学习框架实战(1):图像分类识别

1、绪论 1.1 图像分类的定义 图像分类是计算机视觉领域中的一项基本任务&#xff0c;其定义是将输入图像分配给预定义类别中的一个或多个。具体来说&#xff0c;图像分类系统接受一个图像作为输入&#xff0c;并输出一个或多个类别标签&#xff0c;这些标签描述了图像中的内容…

华为设备RIP基础路由实验

华为设备RIP基础路由实验 实验拓扑&#xff1a; RIP&#xff1a;距离矢量的动态路由&#xff0c;路由通信有方向&#xff0c;度量值metric取值范围&#xff08;1-16&#xff09;16表示目标主机不可达。 路由的版本分为&#xff1a;RIPV1&#xff08;广播通信目标地址是255.255…

Mysql树形结构递归查询

Mysql树形结构递归查询 1.表的基础数据2.基础查询语句3.Mysql8递归查询 1.表的基础数据 类似于这种三级目录&#xff1a; – 1&#xff1a;根结点 – 1-1至1-11 – 1-1-1 至1-1-10 2.基础查询语句 可以使用内联查询inner join去查询&#xff1a; SELECTone.id o…

如何做好流程优化?看这里的目的、原则和方法

流程管理的本质是通过构造卓越的业务流程让流程增值&#xff0c;为客户创造真正的价值。 但卓越的业务流程并不是一蹴而就的&#xff0c;有一个过程&#xff0c;这个过程就是业务流程和流程管理体系不断优化提升的过程&#xff08;可以参照流程成熟度评价模型&#xff09;。 …

React组件通信——兄弟组件

兄弟组件通信 方法一&#xff1a;状态提升 子组件先将数据传递到父组件&#xff0c;父组件再把数据传到另一个子组件中。 import { useState } from "react"; // 定义A组件&#xff0c;向B组件发送数据 function A({ onGetMsg }) {const name "this is A na…

【机器学习】Adaboost: 强化弱学习器的自适应提升方法

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 Adaboost: 强化弱学习器的自适应提升方法引言Adaboost基础概念弱学习器与强学习…

跨境经营的艺术:中资企业海外市场售后服务创新与挑战

出海&#xff0c;已不再是企业的“备胎”&#xff0c;而是必须面对的“大考”&#xff01;在这个全球化的大潮中&#xff0c;有的企业乘风破浪&#xff0c;勇攀高峰&#xff0c;也有的企业在异国他乡遭遇了“水土不服”。 面对“要么出海&#xff0c;要么出局”的抉择&#xff…

【已解决】msi格式无法下载EndNote

背景 windows11家庭中文版&#xff0c;下载EndNote&#xff0c;点击对应的msi文件&#xff0c;显示要用下列哪种方式打开&#xff0c;而不能直接下载。 解决办法 将自己的EndNote的下载文件&#xff08;.msi格式&#xff09;路径&#xff0c;全部设置为英文路径&#xff0c;…

OpenCV轮廓图的一些操作

1.按短边筛选 原始轮廓图&#xff1a; import cv2 import numpy as np# 读取轮廓图 contour_image cv2.imread(..\\IMGS\\pp_edge.png, cv2.IMREAD_GRAYSCALE)# 使用cv2.findContours()函数获取所有轮廓 contours, _ cv2.findContours(contour_image, cv2.RETR_EXTERNAL, cv2…

Firebase Local Emulator Suite详解

文章目录 Firebase Local Emulator Suite 组件安装和使用步骤1. 安装 Firebase CLI2. 初始化 Firebase 项目3. 配置模拟器4. 启动模拟器5. 配置应用程序使用本地模拟器 常见用途 Firebase Local Emulator Suite 是一组本地服务&#xff0c;可以模拟 Firebase 平台的在线服务&am…

《C语言深度解剖》(18):“取整”、“取余“” 和 “取模”的关联与区别?

&#x1f921;博客主页&#xff1a;醉竺 &#x1f970;本文专栏&#xff1a;《C语言深度解剖》 &#x1f63b;欢迎关注&#xff1a;感谢大家的点赞评论关注&#xff0c;祝您学有所成&#xff01; ✨✨&#x1f49c;&#x1f49b;想要学习更多C语言深度解剖点击专栏链接查看&…

word-简历排版

1、确认字体&#xff08;微软雅黑&#xff09;、字号&#xff08;五号/小五&#xff09; 2、设置段间距和行间距、页边距 3、突出各模块标题&#xff0c;增加分格线 4、使用制表位进行对齐&#xff1a;视图-标尺&#xff0c;制表符&#xff08;tab&#xff09;和制表位共同使…

我喜欢的vscode插件

有个更全的&#xff1a;提高编程效率的30个VScode插件 Image preview&#xff08;图片预览&#xff09; any-rule&#xff08;正则表达式大全&#xff09; px to rem & rpx & vw(cssrem)&#xff08;px和rem之间转换&#xff09; 小程序开发助手 Auto Close Tag A…

【C语言】探索文件读写函数的全貌

&#x1f308;个人主页&#xff1a;是店小二呀 &#x1f308;C语言笔记专栏&#xff1a;C语言笔记 &#x1f308;C笔记专栏&#xff1a; C笔记 &#x1f308;喜欢的诗句:无人扶我青云志 我自踏雪至山巅 &#x1f525;引言 本章将介绍文件读取函数的相关知识和展示使用场景&am…