十大排序算法之插入排序、希尔排序、选择排序

news2024/12/23 18:33:20

个人主页:平行线也会相交
欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 平行线也会相交 原创
收录于专栏【数据结构初阶(C实现)】
在这里插入图片描述

本篇主要讲解八大排序算法中的三种排序,分别是:插入排序、希尔排序、选择排序

目录

  • 插入排序
    • 插入排序代码
  • 希尔排序
    • 希尔排序的时间复杂度
    • 希尔排序总代码
  • 选择排序
    • 直接选择排序代码
    • 直接选择排序时间复杂度及总结

插入排序

在我们的日常生活最常见的一个场景就是斗地主,我们在斗地主摸牌的过程中其实就是利用插入排序来整理我们摸到的牌,按照从大到小或者从小到大的进行比较,大的放左边或者小的放左边。斗地主摸牌流程是这样的最初我们拿到第一张牌的时候,我们把这一张牌当成一个有序序列,当我们拿到第二张牌的时候,我们用第二张牌和有序序列进行一一的比较,大的放右边,小的放左边(这里按照升序进行排序),排完第二张牌之后,此时有序序列就变成了两张牌,再用拿到的第三张牌与有序序列进行比较,依次类推,直到排完我们拿到的所有牌。(所以我们摸牌的过程就是一个插入排序
在这里插入图片描述
上图就是插入排序的动态演示图
插入排序思想:把待排序的记录按照其关键码值的大小逐个插入到一个排序好的有序序列中,直到所有的记录插入完为止,最后得到一个新的序列。
下面来正式看插入排序的内容:
直接插入排序的特性:

1.元素集合越接近有序,直接插入排序的算法的时间效率复杂度越高。
2.时间复杂度:O(N^2),最坏的情况就是序列是一个倒序。
3.空间复杂度O(1)。

插入排序代码

#include<stdio.h>
#include<stdlib.h>
void PrintArray(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
}
//插入排序
void InsertSort(int* a, int n)
{
	for (int i = 1; i < n; i++)
	{
		int end = i - 1;//有序的个数
		int tmp = a[i];
		while (end >= 0)
		{
			if (tmp < a[end])
			{
				a[end + 1] = a[end];
				end--;
			}
			else
			{
				break;
			}
		}
		a[end + 1] = tmp;
	}
}
int main()
{
	int a[10] = { 9,1,5,3,7,6,8,2,4,10 };
	InsertSort(a, sizeof(a) / sizeof(a[0]));//插入排序
	PrintArray(a, sizeof(a) / sizeof(a[0]));
	return 0;
}

在这里插入图片描述

希尔排序

在这里插入图片描述
首先,整个希尔排序就分为两个步骤先进行预排序,然后进行插入排序。

我们知道,插入排序算法中如果序列本身已经很接近有序了,那么插入排序是一个不算的算法,那如果序列本身离着有序还很远,此时如果再用插入排序算法的话,效率就会非常低。所以这就引出了希尔排序(对插入排序进行优化)。

希尔排序首先要对序列进行预排序,即分组插入进行排序(间隔为gap的分为一组,gap是几序列就会被分为几组),然后对每组数据进行插入排序。也就是说我们需要进行gap组插入排序。
如下图:
在这里插入图片描述
关于预排序这一步,有两种处理方法一种是一组排完再排另外一组;另一种方法就是多组并排。(代码的话就到最后一同罗列出来把)
第二步就是对序列进行最后的排序。
比如说:我们先以gap=3为间隔进行预排序,最后再调用InsertSort插入函数进行最后的排序。代码就是这样的,请看:

void ShellSort(int* a, int n)
{
	int gap = 3;
	//一组排完再排另一组
	for (int j = 0; j < gap; j++)
	{
		for (int i = gap + j; i < n; i += gap)
		{
			int end = i - gap;;
			int tmp = a[i];
			while (end >= 0)
			{
				if (tmp < a[end])
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			a[end + gap] = tmp;
		}
	}
	for (int k = 0; k < n; k++)
	{
		printf("%d ", a[k]);
	}
	printf("\n");
	InsertSort(a, n);
}

在这里插入图片描述
上述的先进行预排序,然后进行最终的排序算是一种解决方法。但如果我们要排序的是一亿个数呢?我们不可能再像上述那样把gap设置为3。而是对gap进行一个动态的调控,即gap是随着我们不断地对数据进行排序而发生变化。就像下图那样,请看:

在这里插入图片描述

gap越大,跳的就越快,但是接近有序的速度会很慢。
gap越小,跳的就越慢,但是接近有序的速度就会快很多。

那gap到底如何进行取值呢?其实,gap不应该是具体的某个值,gap应该是发生变化的
请看代码:

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

比如,我对下面这个数组进行排序:
在这里插入图片描述
下面是运行结果:
在这里插入图片描述
所以,我们通过对gap进行动态调整可以一次性把这个数组排序完,就没有所谓的第一步第二步了,因为gap/=2,所以最后gap会变成1,就相当于插入排序了。
总结一下

gap>1的时候是对数组进行预排序;当gap==1的时候才是对数组进行直接插入排序。

希尔排序的时间复杂度

关于希尔排序时间复杂度的分析,这里就简单提一嘴,就不细说了。因为希尔排序时间复杂度的分析是很难进行分析的,所以感兴趣的直接去看。这里直接给出希尔排序的结论了:希尔排序的时间复杂度可以按照n1.25~n*n1.25之间来进行计算。

希尔排序总代码

#include<stdio.h>

void PrintArray(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
}

void TestShellSort()
{
	int a[] = { 9,8,7,6,5,4,3,2,1,-5,-2,-6,-4,-2,-6,-9,-1 };

	//PrintArray(a, sizeof(a) / sizeof(a[0]));
	//printf("\n");

	ShellSort(a, sizeof(a) / sizeof(a[0]));
	//PrintArray(a, sizeof(a) / sizeof(a[0]));
	//printf("\n");
}

void ShellSort(int* a, int n)
{
	//int gap = 3;
	一组排完再排另一组
	//for (int j = 0; j < gap; j++)
	//{
	//	for (int i = gap + j; i < n; i += gap)
	//	{
	//		int end = i - gap;;
	//		int tmp = a[i];
	//		while (end >= 0)
	//		{
	//			if (tmp < a[end])
	//			{
	//				a[end + gap] = a[end];
	//				end -= gap;
	//			}
	//			else
	//			{
	//				break;
	//			}
	//		}
	//		a[end + gap] = tmp;
	//	}
		for (int i = 0 + j; i < n - gap; i += gap)
		{
			int end = i;;
			int tmp = a[i + gap];
			while (end >= 0)
			{
				if (tmp < a[end])
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			a[end + gap] = tmp;
		}
	}
	//}
	int gap = n;
	while (gap > 1)
	{
		gap = gap / 3 + 1;
		//gap /= 2;
		//多组并排
		for (int i = 0; i < n - gap; i++)
		{
			int end = i;
			int tmp = a[i + gap];
			while (end >= 0)
			{
				if (tmp < a[end])
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			a[end + gap] = tmp;
		}
		PrintArray(a, n);
		printf("\n");
	}
}

int main()
{
	TestShellSort();
	return 0;
}

在这里插入图片描述

选择排序

插入排序其实就好比我们玩斗地主,在摸牌的时候我们先不对牌进行排序,等到摸完牌之后我们再对手里的这些乱序的牌进行排序,即从大到小或者从小到大进行排序。直接选择排序的过程就是与此相类似。

学习直接选择排序之前我们先来看一看直接选择排序的动态图:
在这里插入图片描述
上图是按照升序进行排序的,即每一轮的比较我们可以把最小的一个数筛选出来放到最左边,但是我们可以对其进行优化,我们筛选最小的数的同时还可以把最大的数筛选出来放到最右边。所以每一轮比较我们可以把最小的和最大的数同时筛选出来分别放到最左边和最右边。
这个排序虽然简单,但是这个排序非常不稳定,实际上也很少用这个排序。

直接选择排序代码

void SelectSort(int* a, int n)
{
	int left = 0;
	int right = n - 1;

	while (left < right)
	{
		int mini = left;
		int maxi = left;
		for (int i = left + 1; i <= right; i++)
		{
			if (a[i] < a[mini])
			{
				mini = i;
			}
			if (a[i] > a[maxi])
			{
				maxi = i;
			}
		}
		Swap(&a[left], &a[mini]);
		if (left == maxi)
		{
			maxi = mini;
		}
		Swap(&a[right], &a[maxi]);
		left++;
		right--;
	}
}

关于这个直接选择排序的话,有一点需要我们特别注意。就是当我们的leftmaxi重叠的时候,请看举例:
在这里插入图片描述
上面举例中,mini5maxi3没有错,这里特殊就特殊在这里的maxileft重叠了,所以当我们的left和mini交换完之后,即:
在这里插入图片描述
此时交换前的mini正好把处于left位置的maxi进行覆盖了,所以下次交换就会导致出现错误。
请看:
在这里插入图片描述
上图中的a[6]的位置本应该是6,但是就是因为maxileft重叠了,所以就会出错,所以我们在进行第二次交换之前需要判断是否maxileft会重叠,即:

if (left == maxi)
{
	maxi = mini;
}

这就是直接交换排序比较容易忽略的一个点,需要我们特别注意。
最终运行结果如下:
在这里插入图片描述

直接选择排序时间复杂度及总结

选择排序时间复杂度的话,无论最好情况还是最坏情况时间复杂度都是O(N^2)
总结一下直接选择排序的特点:

1.我们单单从时间复杂度的角度来看就可以知道直接选择排序的效率并不高。因为无论数据有序还是比较有序又或者是乱序对于直接选择排序而言都没有太大的区别。所以直接选择排序真的是毫无技巧可言,直接硬生生地遍历一遍数据,给我一种软硬不吃的感觉😄。
2.时间复杂度:O(N^2)
3.空间复杂度:O(1)

以上就是八大排序算法的一部分,主要讲解的是插入排序、希尔排序、选择排序
好了,就到这里吧,一起加油,再见啦各位!!!
在这里插入图片描述

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

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

相关文章

家用洗地机有什么优缺点?实用的洗地机分享

随着科技的不断发展&#xff0c;家庭清洁设备也在不断更新换代。现在市场上最常见的家用清洁设备包括洗地机、扫地机器人和吸尘器。这些设备各有优缺点&#xff0c;但在清洁效果、清洁范围和清洁方式等方面存在差异。洗地机是一种专业的清洁设备&#xff0c;它能够深度清洁地面…

SpringCloud之Gateway组件简介

网关的理解 网关类似于海关或者大门&#xff0c;出入都需要经过这个网关。别人不经过这个网关&#xff0c;永远也看不到里面的东西。可以在网关进行条件过滤&#xff0c;比如大门只有对应的钥匙才能入内。网关和大门一样&#xff0c;永远暴露在最外面 不使用网关 前端需要记住每…

Javascript进阶专题总结(函数、异步编程、设计模式)

函数式编程什么时候用 编程方法&#xff1a;函数式(js)&#xff0c;面向对象(java,c)&#xff0c;命令式 函数式&#xff08;工具式操作&#xff09; 优点&#xff1a;JavaScript种函数是一等公民&#xff0c;便于拆分组合可扩展性好&#xff0c;方便tree-shaking 缺点&…

【Linux系统】系统安全及应用二

开关安全控制一、开个安全控制1.1调整BIOS引导设置1.2GRUB限制1.3终端安全控制二、系统弱口令检查2.1安装JR工具三、网络端口扫描3.1NMAP端口扫描3.2NETSTAT&#xff0c;SS查看端口信息一、开个安全控制 1.1调整BIOS引导设置 将第一引导设备设为当前系统所在硬盘禁止从其他设…

1-时间复杂度分析

时间复杂度 ①what&#xff1a; 指执行当前算法所消耗的时间 ②简介结论&#xff1a; 时间复杂度由多项式T(n)中最高阶的项来决定&#xff0c;系数的影响忽略即可 例子&#xff1a; 操作数量T(n) 时间复杂度O(f(n)) 常数&#xff0c;比如 100000&#xff08;即&#xff1…

ChatGPT将批量文档翻译成中文的方法

文档翻译成中文软件是指在处理文档时&#xff0c;自动将文档中的内容翻译成中文的软件。这些软件通常采用自然语言处理技术&#xff0c;通过对待翻译文本的分词、词义分析、语法分析等多种技术处理&#xff0c;实现对文本中的单词、短语、句子等级别的翻译。 文档翻译成中文软件…

CSDN,有幸遇见 ——我的三周年创作纪念日

一生二&#xff0c;二生三&#xff0c;三生万物 三年&#xff0c;与 CSDN 的相遇相识相知—— 前两年都没写&#xff0c;一是没纪念的概念&#xff0c;二是纪念日这天每年总是清明节。 还有就是不知道有勋章&#xff08;&#xff08; Meet 遇见 那年&#xff0c;我六年级&a…

JVM 垃圾回收详解之垃圾收集算法+垃圾收集器

标记-清除算法 该算法分为“标记”和“清除”阶段&#xff1a;首先标记出所有不需要回收的对象&#xff0c;在标记完成后统一回收掉所有没有被标记的对象。 它是最基础的收集算法&#xff0c;后续的算法都是对其不足进行改进得到。 这种垃圾收集算法会带来两个明显的问题&…

Node.js—path模块

目录 1、什么是path模块2、path.resolve 拼接规范的绝对路径 常用3、path.sep 获取操作系统的路径分隔符4、 path.parse 解析路径并返回对象5、path.basename 获取路径的基础名称6、path.dirname 获取路径的目录名7、path.extname 获得路径的扩展名8、 path.join()路径拼接 1、…

Linux高性能服务器编程|阅读笔记:第2章 - IP协议详解

简介 Hello! 非常感谢您阅读海轰的文章,倘若文中有错误的地方,欢迎您指出~ ଘ(੭ˊᵕˋ)੭ 昵称:海轰 标签:程序猿|C++选手|学生 简介:因C语言结识编程,随后转入计算机专业,获得过国家奖学金,有幸在竞赛中拿过一些国奖、省奖…已保研 学习经验:扎实基础 + 多做笔…

超越ChatGPT:AgentGPT正在将自主AI带到浏览器中

你好&#xff0c;欢迎来到人工智能领域的新时代&#xff01;今天我们介绍AgentGPT&#xff0c;这是一款最前沿的基于浏览器的平台&#xff0c;旨在革新人工智能的自主性。这项开创性的技术让你能够在舒适的网络浏览器中创建、配置和部署定制化的人工智能代理&#xff0c;以实现…

又涨了?2023全国程序员薪资最新统计(文末附招聘岗位)

大家好&#xff0c;金三银四招聘季还在进行中。刚好最近看到一份 2022 国内程序员薪酬报告&#xff0c;感觉挺有意思的&#xff0c;跟大家分享一下。 在科技迅速发展的时代&#xff0c;各行业对程序员的需求持续增长&#xff0c;程序员作科技市场的“重要基石”&#xff0c;薪…

nodejs+vue 高校校园食堂餐品在线订购网

食堂作为学校的一个重要的部门&#xff0c;为学生提供了用餐的地点&#xff0c;学生可以在食堂享用丰富的餐品&#xff0c;建立一个在校订餐网站&#xff0c;帮助了学生提供一个用餐订餐的系统&#xff0c;也帮助了食堂提供了一个餐品展示的站点。 园的食堂作为一个窗口单位&a…

【nacos配置中心】源码部分解析

启动初始化 SpringApplication.prepareContext applyInitializers 回调ApplicationContextInitializer的initialize方法 getInitializers()从applicationContext获取List<ApplicationContextInitializer<?>> initializers 这个集合是通过SpringApplication的…

Node.js -- Express中间件

1.中间件的概念 1.1 什么是中间件 中间件&#xff08;Middleware&#xff09;,特指业务流程的中间处理环节。 1.2 现实中的例子 1.3 Express中间件的调用流程 当一个请求到达Express服务器中&#xff0c;可以连续调用多个中间件&#xff0c;从而对这次请求进行预处理。 1…

OpenGL入门教程之 深入理解

一、OpenGL简介 OpenGL是一种用于渲染2D、3D矢量图形的跨语言、跨平台的应用程序编程规范。OpenGL包含一系列可以操作图形和图像的函数&#xff0c;但OpenGL没有实现这些函数&#xff0c;OpenGL仅规定每个函数应该如何执行以及其输出值(类似接口)&#xff0c;所以OpenGL仅是一…

Excel技能之计数求和,让你成为高手之路的机车手

Excel高手之路&#xff0c;开始学习就看到希望。 直接套用&#xff0c;秒变Excel大神。点滴积累&#xff0c;效率飞升。分享给更多的人&#xff0c;一起学习进步。告别加班&#xff0c;让同事刮目相看。 Excel函数&#xff0c;你可以不用&#xff0c;但是不能不知道&#xff…

OpenAI-ChatGPT最新官方接口《安全最佳实例》全网最详细中英文实用指南和教程,助你零基础快速轻松掌握全新技术(十)(附源码)

Safety best practices 安全最佳实践 前言Use our free Moderation API 使用我们的免费审核APIAdversarial testing 对抗性测试Human in the loop (HITL) 人在回路Prompt engineering 快速工程“Know your customer” (KYC) “了解你的客户”Constrain user input and limit ou…

【Vue3】setup的注意点及watch监视属性的六种情况

一&#xff0c;setup须知 1.1setup的执行时间 1.setup的执行时间要比beforCreate执行要早 export default {name: "Demo",beforeCreate(){console.log(beforeCreate已执行);},setup() {console.log(setup已执行);let person reactive({name: "小明",ag…

Faster RCNN系列——RPN的真值详解与损失值计算

目录 一、RPN真值详解二、RPN预测值三、损失值计算 一、RPN真值详解 RPN的真值分为类别真值和偏移量真值&#xff0c;即每一个Anchor是否对应着真实物体&#xff0c;以及每一个Anchor对应物体的真实偏移值&#xff0c;这两种真值的具体求解过程如下图所示&#xff1a; Anchor生…