数据结构之插入排序

目录

前言

插入排序

直接插入排序

插入排序的时间复杂度

希尔排序 


前言

在日常生活中,我们不经意间会遇到很多排序的场景,比如在某宝,某东上买东西,我们可以自己自定义价格是由高到低还是由低到高,再比如在王者某耀中的每个英雄的荣耀战力,都是由高到低进行排序的,这些场景都用到了排序,但是这些场景的底层都是用一个个排序算法来实现的,本期开始,我们就要学习数据结构中很重要的一个知识点-------排序。

 几种常见的排序:

注:我们今后所有的排序都默认排升序。

本期我们主要讲解插入排序。

插入排序

直接插入排序

直接插入排序在现实生活中最常见的应用其实就是斗地主时,我们一般会将牌拍好序,然后根据自己的打法打牌,大家先仔细想想,再给牌整理时我们是怎样整理的呢?当我们摸到第一张牌时,此时它已经有序,我们摸第二张牌时,就会与第一张牌做比较,然后发生交换使之成为我们想要的顺序,当我们摸第三张牌时,我们又会与最后一张牌与倒数第二张牌进行比较,交换之后使牌的顺序成为我们想要的顺序,之后每摸一张牌,就按照此方法进行整理,最后就会得到一个有序的牌列。这就直接插入排序的一个比较常见的应用场景。在数据结构中,我们怎样实现直接插入排序呢?

要实现直接插入排序,我们可以先将直接插入排序分解为单趟排序,然后再让每趟排序组合成为整个排序。

单趟排序:

单趟排序的情景:我们要将一个元素插入一个有序数组之中。可以类比,我们摸了一张牌还没有插入手中的牌中,但是手中的牌肯定已经是一个有序的牌序列了。

单趟排序的算法思想:我们令有序的数组的最后一个元素的位置为end,要插入的元素为x。要将x插入一个有序数组,就得先让x与end位置上的元素进行比较,因为我们是排升序,所以如果x比a[end]小,就要把a[end]向后挪动一个位置,然后将end--,然后在让x与此时end位置上的元素a[end]进行比较,比较的原理同上,直到将x插入到合理的位置,那么怎样判断这样的一趟排序已经结束了呢?其实就是x比当前end位置上的元素要大,这是一个结束条件,还有就是x比有序数组所有的元素都要小,即就是要将x放在数组的第一个元素的位置上,此时end已经到了数组第一个元素位置的前一个位置之前,我们称此时end==-1,所以综上,一趟排序的结束标志就是x>a[end]或者end<0

单趟排序代码实现:

int end = size - 1;
	int x;
	while (end >= 0)
	{
		if (a[end] > x)
		{
			a[end + 1] = a[end];
			end--;
		}
		else 
		{
			break;
		}
	}
	a[end + 1] = x;

 到了这里,我们单趟排序已经搞定了,但是上面我们已经说了,我们把整个插入排序分为了每趟排序,那么怎样将每趟排序合并成为直接插入排序呢?

每趟排序的关键点在于是把一个元素插入一个有序数组之中,其实对于一个数组而言,无论它是否是有序还是无序的,其第一个元素毋庸置疑肯定是有序的,这便是关键,我们可以从第一个与元素开始,把第一个元素当成一个有序数组,此时第二个元素就是要插入有序数组中的元素,那么此时第二哥元素插入由第一个元素组成的有序数组中就可以看成是一趟排序,这趟排序之后,第一个元素和第二个元素就组成了一个有序数组,第三个元素插入由第一个和第二个元素组成的有序数组就可以看成第二趟排序,由此依次进行多趟排序,那么整个数组的多趟排序就最终组成了直接插入排序。

直接插入排序完整代码:

void InsertSort(int* a, int size)
{
	assert(a);
	for(int end=0;end<size-1;end++)
	{ 
		int x = a[end + 1];
		while (end >= 0)
		{
			if (a[end] > x)
			{
				a[end + 1] = a[end];
				end--;
			}
			else
			{
				break;
			}
		}
		a[end + 1] = x;
	
	}
}
int main()
{
	int arr[] = { 10,9,8,7,6,5,4,3,2,1 };
	InsertSort(arr, sizeof(arr) / sizeof(int));
	for (int i = 0; i < sizeof(arr) / sizeof(int); i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;

}

运行截图如下:

注意:

排序这里比较重要的就是边界的控制,对于直接插入排序而言,若数组的大小为sizeend的初始值就是0end的最终值就是size-2x的初始位置就是1x的最终位置就是size-1。

以上便是直接插入排序的整个过程。

插入排序的时间复杂度

最好:O(N),已经有序的数组,要插入的元素比end位置上的元素大,只用跟end位置上的元素比较一下。

最坏:O(N^2),逆序,要插入的元素比有序数组的所有元素都要小,跟end位置和end位置之前的所有元素都要比较一下。

稳定性:稳定。

希尔排序 

希尔排序其实就是插入排序的进阶版,我们知道,一个数组越有序,直接插入排序的时间复杂度越低,希尔排序其实就是为了将一个无序的数组变得有序,使插入排序变得更加简单。

希尔排序如图:

希尔排序的思想:先设置一个gap值,先把整个数组的元素分成gap组,即下标差gap的为一组,然后对每一组按照直接插入排序的思想进行排序,每一组的排序将完成一次预排序,每完成一次预排序,数组会变得比之前更加有序。我们再改变gap的值多次进行预排序,当gap==1时,就是直接插入排序,此时因为经历了多次预排序,整个数组已经变得有序,所以此时再用直接插入排序对整个数组完成最终的排序。

希尔排序的整体代码:

void ShellSort(int* a, int size)
{
	int gap = size;
	while (gap > 1)
	{
		gap /= 2;
		//进行一趟预排序
		for (int i = 0; i < gap; i++)
		{
			//进行每组排序
			for (int end = i; end < size - gap; end += gap)
			{
				int x = a[end + gap];
				//进行单趟排序
				while (end >= 0)
				{
					if (a[end] > x)
					{
						a[end + gap] = a[end];
						end -= gap;
					}
					else
					{
						break;
					}
				}
				a[end + gap] = x;
			}
		}
	}
}
int main()
{
	int arr[] = { 10,9,2,8,6,5,4,3,11,1 };
	ShellSort(arr, sizeof(arr) / sizeof(int));
	for (int i = 0; i < sizeof(arr) / sizeof(int); i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

 运行截图如下:

当然,上述的方法是把三组的排序是分开的,每组自己排自己的,但是下面这种方法就是三种排序我们再一次排序中就全部排序完成,更为简洁,但是没有第一种好理解,可以理解为是第一种方法的进阶版本,但是这种进阶版本是我们经常使用的。

进阶版本代码如下:

void ShellSort(int* a, int size)
{
	int gap = size;
	while (gap > 1)
	{
		gap /= 2;
		//进行一趟预排序,将多组排序在一次排序中就完成了,而不是按组进行排序
			for (int end = 0; end < size - gap; end++)
			{
				int x = a[end + gap];
				//进行单趟排序
				while (end >= 0)
				{
					if (a[end] > x)
					{
						a[end + gap] = a[end];
						end -= gap;
					}
					else
					{
						break;
					}
				}
				a[end + gap] = x;
			}
	}
}
int main()
{
	int arr[] = { 10,19,2,8,6,0,4,3,11,1 };
	ShellSort(arr, sizeof(arr) / sizeof(int));
	for (int i = 0; i < sizeof(arr) / sizeof(int); i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

以上便是希尔排序的整个过程。

希尔排序的时间复杂度

 希尔排序的时间复杂度为:

1.最好:O(N),当整个数组为有序数组时,复杂度最小。排序算法的时间复杂度的天花板就是O(N)

 2.最坏:总共分成了gap组,每组大概就是size/gap个元素,如果每组都逆序,那么每组的复杂度又是一个1+2+...+size/gap的等差数列,平均是O(N^1.3)。

稳定性:不稳定。

插入排序的内容就是这些,我们需要注意的仍然是算法中边界的控制,在编写代码时,我们可以自己画图去控制边界,这是一个很好的方法。

好了,本期的内容到此结束^_^ 

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

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

相关文章

机器连接和工业边缘计算

软件应用和IT创新是制造业投资的主要驱动力。解决方案架构应围绕特定标准进行整合&#xff0c;并采用架构蓝图和最佳实践来满足最终用户的需求。此外&#xff0c;边缘计算&#xff08;Edge Computing&#xff09;也将在制造业中加速部署。 边缘计算是制造业的下一个变革驱动力。…

设计模式篇之创建型模式

目录 前言一、简单工厂模式二、工厂方法模式总结 前言 最近开始整理Java设计模式&#xff0c;本篇主要分享设计模式中的创建型模式&#xff0c;并给出demo代码&#xff0c;适合初中级开发学习。分享书籍《大话设计模式》&#xff0c;分享GitHub学习设计模式仓库。 一、简单工厂…

Zookeeper(服务注册中心)安装以及启动服务

概述 ZooKeeper是一个分布式的开源协调服务&#xff0c;用于管理和协调大规模分布式系统中的各种任务。它提供了一个简单的分层命名空间&#xff0c;以及对数据的强一致性&#xff08;ACID特性&#xff09;和高可用性的支持。 ZooKeeper提供了一个类似文件系统的层次结构&…

智能优化算法应用:基于混沌博弈算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于混沌博弈算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于混沌博弈算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.混沌博弈算法4.实验参数设定5.算法结果6.参考…

从零开始的Spring Cloud Gateway指南:构建强大微服务架构

目录 一、 什么是Gateway&#xff1f;1. 网关的由来2. 网关的作用3. 网关的技术实现 二、如何搭建一个简易网关服务1. 引入依赖2. 配置yml文件 三、进阶话题&#xff1a;过滤器和路由配置1. gateway的执行原理2. 路由断言工厂: Predicate Factory3. 网关过滤器&#xff1a;Gate…

UE Windows平台下Linux的交叉编译项目打包

UE Windows平台下Linux的交叉编译项目打包 交叉编译&#xff08;Cross-compilation&#xff09; 使得在以Windows为中心的工作流程中工作的游戏开发者能够以Linux为目标对项目进行打包。目前&#xff0c;只有Windows支持交叉编译。 交叉编译支持的平台 Windows | Linux-x86_…

java8 常用code

文章目录 前言一、lambda1. 排序1.1 按照对象属性排序&#xff1a;1.2 字符串List排序&#xff1a;1.3 数据库排序jpa 2. 聚合2.1 基本聚合&#xff08;返回对象list&#xff09;2.2 多字段组合聚合&#xff08;直接返回对象list数量&#xff09; 二、基础语法2.1 List2.1.1 数…

附录B 存储次层次结构回顾

1. 引言 缓存是指地址离开处理器后遇到的最高级或第一级存储器层次结构。 如果处理器在缓存中找到了所请求的数据项&#xff0c;就说发生了缓存命中。如果处理器没有在缓存中找到所请求的数据项&#xff0c;就说发生了缓存缺失。此时&#xff0c;包含所请求的字的固定大小的数…

五年制专转本经验分享

五年制专转本经验分享 提早设定学习目标&#xff0c;会让你努力更有方向&#xff01;同学们在大一、大二时不要以为专转本离你还很远&#xff0c;时际上根据我们十多年的培训经验来说&#xff0c;专转本备考越早准备越有利于通过&#xff01;大家知道英语科目的话&#xff0c;在…

LeetCode437.路径总和III

看完题目我就拿直接用递归写了如下代码&#xff1a; class Solution {private int ans;public int pathSum(TreeNode root, int targetSum) {ans 0;dfs(root, targetSum, 0);return ans;}public void dfs(TreeNode root, int targetSum, int sum){if(root null)return;sum r…

Python中判定列表是否包含某个元素的方法

更多Python学习内容&#xff1a;ipengtao.com 大家好&#xff0c;我是彭涛&#xff0c;今天为大家分享 Python中判定列表是否包含某个元素的方法&#xff0c;全文4000字&#xff0c;阅读大约10分钟。 在Python编程中&#xff0c;判定一个列表是否包含特定元素是一项常见任务。本…

界面控件DevExpress WPF导航组件,助力升级应用程序用户体验!(上)

DevExpress WPF的Side Navigation&#xff08;侧边导航&#xff09;、TreeView、导航面板组件能帮助开发者在WPF项目中添加Windows样式的资源管理器栏或Outlook NavBar&#xff08;导航栏&#xff09;&#xff0c;DevExpress WPF NavBar和Accordion控件包含了许多开发人员友好的…

Apache或Nginx在Linux上配置虚拟主机

在Linux上使用Apache或Nginx配置虚拟主机可以让您在同一台服务器上托管多个网站。这样不仅可以充分利用服务器资源&#xff0c;还能降低每个网站的运营成本。以下是使用Apache和Nginx配置虚拟主机的步骤。 使用Apache配置虚拟主机 安装Apache服务器软件。在终端中使用以下命令…

『亚马逊云科技产品测评』活动征文|基于亚马逊EC2云服务器配置Nginx静态网页

授权声明&#xff1a;本篇文章授权活动官方亚马逊云科技文章转发、改写权&#xff0c;包括不限于在 Developer Centre, 知乎&#xff0c;自媒体平台&#xff0c;第三方开发者媒体等亚马逊云科技官方渠道 亚马逊EC2云服务器&#xff08;Elastic Compute Cloud&#xff09;是亚马…

JSON 语法详解:轻松掌握数据结构(下)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

金融科技走向 Web3 的趋势不可逆转——新加坡金融科技节会后总结(上)

11 月 15 日至 17 日&#xff0c;2023 年度新加坡金融科技节&#xff08;Singapore FinTech Festival&#xff0c;以下简称 SFF&#xff09;在樟宜机场附近的新加坡会展中心&#xff08;Singapore Expo&#xff09;举办。我本人受新加坡金管局的邀请&#xff0c;第一次亲身参与…

百度APP iOS端包体积50M优化实践(七)编译器优化

一. 前言 百度APP iOS端包体积优化系列文章的前六篇重点介绍了包体积优化整体方案、图片优化、资源优化、代码优化、无用类优化、HEIC图片优化实践和无用方法清理&#xff0c;图片优化是从无用图片、Asset Catalog和HEIC格式三个角度做深度优化&#xff1b;资源优化包括大资源…

制作一个RISC-V的操作系统四-嵌入式开发介绍

文章目录 什么是嵌入式开发交叉编译查看一些GCC文件夹 调试器GDB相关语法命令 模拟器QEMUQEMU的安装和使用项目构造工具MakeMakeFile的构成make的运行 练习4-1联系4-2练习4-3 什么是嵌入式开发 程序跑到开发板上&#xff0c;或者说运行到硬件上 交叉编译 简单理解交叉编译来说…

(2/2)敏捷实践指南 Agile Practice Guide ([美] Project Management institute 著)

附录 A1 - 《PMBOK指南》映射 表A1显示了第六版《PMBOK指南》中定义的项目管理过程组与知识领域之间的对应关系 本附录说明了如何利用混合和敏捷方法处理《PMBOK指南》知识领域&#xff08;请参见表A1-2&#xff09;中所述的属性&#xff0c;其中涵盖了相同和不同的属性&…

概率密度函数(PDF)正态分布

概率密度函数&#xff08;PDF&#xff09;是一个描述连续随机变量取特定值的相对可能性的函数。对于正态分布的情况&#xff0c;其PDF有一个特定的形式&#xff0c;这个形式中包括了一个常数乘以一个指数函数&#xff0c;它假设误差项服从均值为0的正态分布&#xff1a; p ( …
最新文章