DS:八大排序之直接插入排序、希尔排序和选择排序

                                         创作不易,感谢三连支持!! 

一、排序的概念及运用

1.1 排序的概念

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

关于这些基础概念我会在后面慢慢介绍! 

1.2 排序的运用

我们在淘宝购买商品的时候,可以选择让商品根据销量、信用、价格、综合程度进行排序

 还有高校排名,以及考试的排名,都是通过排序来完成的!!

排序存在的意义:帮助我们筛选出最优的选择

1.3 常见的排序算法

二、直接插入排序

2.1 思路

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

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

2.2 直接插入排序的实现

        当插入第i(i>=1)个元素时,前面的array[0],array[1],…,array[i-1]已经排好序,此时用array[i]的排序码与array[i-1],array[i-2],…的排序码顺序进行比较,找到插入位置即将array[i]插入,原来位置上的元素顺序后移

           我们先按照上面的思路,先模拟摸一张牌的过程,假设目前手上的牌是2 4 9 然后摸到了1张3,我们设置最后一张牌9的下标位置为end(2),然后让新摸的牌为temp(a[3]),开始慢慢往前比较,发现较大的就交换位置。

    int end=2;
	int temp=a[3];
	while (end >= 0)
	{
		if (a[end] > temp)//如果前面的数比后面的数大,就前面元素插入到后面的位置
			{        
              a[end + 1] = a[end];
             --end;
              }
		else
			break;
		
	}
	a[end+1] = temp;//不写在循环里面,是避免end减成-1,此时说明新加入的牌是最小的,正好放在一开始的位置

 上述过程可以实现插入一张牌,那么整体的实现就在外面加个for循序即可!!

void InsertSort(int* a, int n)
{
	for (int i = 0; i < n - 1; i++)
	{
		int end = i;
		int temp = a[i+1];
		while (end >= 0)
		{
			if (a[end] > temp)//如果前面的数比后面的数大,就前面元素插入到后面的位置
				a[end + 1] = a[end];
			else
				break;
			--end;
		}
		a[end + 1] = temp;//不写在循环里面,是避免end减成-1,此时说明新加入的牌是最小的,正好放在一开始的位置
	}
}

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

2.3 复杂度分析

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

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

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

三、希尔排序

3.1 思路

         希尔排序其实是直接插入排序的一种变形,我们知道对于直接插入排序来说,最坏的情况就是逆序,此时的时间复杂度就是O(N^2),最好的情况是接近有序,此时时间复杂度为O(N),这个时候希尔有了一个想法:有没有一种方法可以让一组无序的数据经过处理后使他接近有序,然后再最后实现一次直接插入排序呢?

      最后希尔发明出来了希尔排序

3.2 希尔排序的实现

具体思路:

1、对无序的数组进行预排序,使其接近有序。

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

 这里的预排指的是:间隔gap的元素为一组,总计gap组,我们先假设gap为3,然后我们画个图来理解一下:

 根据我们之前写的直接插入排序算法,我们可以先实现将红色的一组进行排序的算法

int gap = 3;
for (int i = 0; i < n - gap; i+=gap)
{
	int end = i;
	int temp = a[i + gap];
	while (end >= 0)
	{
		if (a[end] > temp)//如果前面的数比后面的数大,就前面元素插入到后面的位置
			a[end + gap] = a[end];
		else
			break;
		end -= gap;
	}
	a[end + gap] = temp;
}

 我们发现,如果我们一开始让i=1,就可以实现蓝色组的排序,让i=2的话,就可以实现绿色组的排序,所以为了让三组都完成排序,我们再外面再嵌套一层循环!

int gap = 3;
for (int j = 0; j < gap; j++)
{
	for (int i = j; i < n - gap; i += gap)
	{
		int end = i;
		int temp = a[i + gap];
		while (end >= 0)
		{
			if (a[end] > temp)//如果前面的数比后面的数大,就前面元素插入到后面的位置
				a[end + gap] = a[end];
			else
				break;
			end -= gap;
		}
		a[end + gap] = temp;
	}
}

这样我们就实现了三组的预排序了!! 

但其实上面的代码还可以优化成两层循环!!

int gap = 3;
	for (int i = 0; i < n - gap; i ++)
	{
		int end = i;
		int temp = a[i + gap];
		while (end >= 0)
		{
			if (a[end] > temp)//如果前面的数比后面的数大,就前面元素插入到后面的位置
				a[end + gap] = a[end];
			else
				break;
			end -= gap;
		}
		a[end + gap] = temp;
	}

刚刚那种写法是一组一组去完成预排,而现在这种写法是实现多组并排,效果是一样的!! 

这样的预排序有什么意义呢?

1、 gap越大,大的数可以更快到后面,小的数可以更快到前面,但是越不接近有序

2、gap越小,大的小的就挪动的越慢,但是也越接近有序

3、gap==1时,就是直接插入排序(我们可以发现当gap等于1时,这个预排序算法与直接插入排序算法的写法是一样的!!)

现在来分析gap该取多少合适?

     首先,gap是不能随便取的,因为比如说有100万个数据,gap取3,显然是不合适的,所以我们的gap一定要跟数据个数n建立联系,gap具体取多少是最合适的没有得到很好的证明,所以我们使用Knuth的思路来将我们的希尔排序完善好!!

void ShellSort(int* a, int n)
{
	//gap>1  预排序
	//gap=1 直接插入排序
	int gap = n;
	while (gap > 1)
	{
		gap = gap / 3 + 1;//这是为了保证gap最后一定为0
		for (int i = 0; i < n - gap; i++)
		{
			int end = i;
			int temp = a[i + gap];
			while (end >= 0)
			{
				if (a[end] > temp)//如果前面的数比后面的数大,就前面元素插入到后面的位置
					a[end + gap] = a[end];
				else
					break;
				end -= gap;
			}
			a[end + gap] = temp;
		}
	}
}

需要注意的是:gap = gap / 3 + 1是为了保证gap最后一定会等于1,也就是一定会在最后进行一次直接插入排序,保证有序,而前面gap>1的过程都是在进行预排序!!

3.3 复杂度分析

因为预排是一个逐渐转好的过程,所以我们还按照最坏情况去考虑是不合理的,因此这边是难以计算的,我们看看书上的讲解

 《数据结构(C语言版)》--- 严蔚敏

《数据结构-用面相对象方法与C++描述》--- 殷人昆
 

因为咋们的gap是按照Knuth提出的方式取值的,而且Knuth进行了大量的试验统计,我们暂时就按o(N^1.25)到o(1.6N^1.25)到来算

四、选择排序

4.1 思路

选择排序的思想:每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完 。

      这个其实也跟摸扑克牌有关,但是这次跟直接插入排序不一样的是,直接插入排序是一次摸一张牌然后插入调整,而选择排序是一次性拿了所有牌,再逐个把小的数往前放!

4.2 选择排序的实现

1、在元素集合array[i]--array[n-1]中选择关键码最大(小)的数据元素

2、若它不是这组元素中的最后一个(第一个)元素,则将它与这组元素中的最后一个(第一个)元素交换
3、在剩余的array[i]--array[n-2](array[i+1]--array[n-1])集合中,重复上述步骤,直到集合剩余1个元素

我们拿到所有的牌后,每次都把最小的牌往前放

void SelectSort(int* a, int n)
{
	for (int begin = 0; begin < n; begin++)
	{
		int min = begin;//记录最小元素的下标
		for (int i = begin+1; i < n; i++)
		{
			if (a[min] > a[i])
				min = i;//记录最小的牌的下标
		}
		Swap(&a[begin], &a[min]);
	}
}

 但是每次遍历就记一张最小的牌,效率太低下了,所以我们改造一下该算法,使得该算法每遍历一次就记住最小的牌和最大的牌,然后分别放在两边!!

void SelectSort(int* a, int n)
{
	int left = 0; 
	int right = n - 1;
	while (left < right)
	{
		int min = left;
		int max = left;
		for (int i = left+1; i <= right; i++)
		{
		
			if (a[min] > a[i])
			    min = i;
		    if (a[max] < a[i])
				max = i;
		}
		//这里要考虑一种情况,就是如果最大的数恰好就在最左端,那么就会导致第二次swap换到后面的就不是最大的数而是最小的数了
		Swap(&a[min], &a[left]);
		//如果max和begin重叠,修正一下
		if (max == left)
			max = min;
		Swap(&a[max], &a[right]);
		left++;
		right--;
	}
}

易错点1:min和max要从他们后面的第一张牌开始去一张一张比较 

易错点2:交换的时候,如果最大的元素恰好在最左边,那么就有可能被最小的元素给交换过去了,所以这个时候要注意及时地修正!!

4.3 复杂度分析

时间复杂度:O(N^2)

单趟无论选择一个还是选择两个,都得遍历一遍,复杂度为O(N),整体还得遍历一遍O(N)

空间复杂度:O(1)

 

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

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

相关文章

GPT翻译网站的加载与使用

Sider: ChatGPT侧边栏 GPTs, GPT-4 Turbo, 联网, 绘图 sider.ai https://chromewebstore.google.com/detail/sider-chatgpt%E4%BE%A7%E8%BE%B9%E6%A0%8F-gpts-g/difoiogjjojoaoomphldepapgpbgkhkb?hlzh-CN 加入与移除 第二个翻译网站 https://chromewebstore.google.com/…

[BUUCTF]-PWN:ciscn_2019_es_1解析(tcachebin duf)

查看保护 再查看ida 大致为alloc创建堆块&#xff0c;free释放堆块&#xff0c;show输出堆块内容 但是要注意一点free没有清空堆块指针 完整exp&#xff1a; from pwn import* from LibcSearcher import* pprocess(./es1) premote(node5.buuoj.cn,26841)def alloc(size,cont…

红色警戒 3 修改游戏速度

原文&#xff1a;https://blog.iyatt.com/?p13852 红警 2 是有提供游戏速度修改的&#xff0c;红警 3 没有&#xff0c;而且游戏速度似乎和 FPS 关联的&#xff0c;在配置低一些的电脑上会变慢&#xff0c;FPS 也降低&#xff0c;我电脑上开最高画质 FPS 不超过 30&#xff0c…

102.网游逆向分析与插件开发-网络通信封包解析-解读喊话道具数据包并且利用Net发送

内容参考于&#xff1a;易道云信息技术研究院VIP课 上一个内容&#xff1a;解读聊天数据包并且利用Net发送 码云地址&#xff08;游戏窗口化助手 分支&#xff09;&#xff1a;https://gitee.com/dye_your_fingers/sro_-ex.git 码云版本号&#xff1a;cc6370dc5ca6b0176aafc…

TiDB in 2023, 一次简单的回顾丨PingCAP 唐刘

2023 年已经过去&#xff0c;TiDB 经过了一年的迭代&#xff0c;又往前进步了一点点&#xff0c;我们非常自豪的看到&#xff0c;TiDB 正在不断地帮助我们的客户成功&#xff0c;包括但不限于&#xff1a; ○ 首个云原生、分布式、全栈国产化银行核心业务系统投产上线丨TiDB …

云计算基础-快照与克隆

快照及克隆 什么是快照 快照是数据存储的某一时刻的状态记录&#xff0c;也就是把虚拟机当前的状态保存下来(快照不是备份&#xff0c;快照保存的是状态&#xff0c;备份保存的是副本) 快照优点 速度快&#xff0c;占用空间小 快照工作原理 在了解快照原理前&#xff0c;…

leetcode135. 分发糖果

leetcode135. 分发糖果 题目 思路 两者兼顾很容易顾此失彼&#xff0c;因此分别考虑两方向&#xff0c;初始化为全1数组从前向后遍历&#xff1a;只要右边评分比左边大&#xff0c;result[i] result[i-1] 1从后向前遍历&#xff1a;只要左边评分比右边大&#xff0c;result…

round sphere around ground background space-around space-between space-evenly

round sphere around ground background space-around space-between space-evenly round around ground surround round sphere around ground background around surround around evenly between space-around space-between space-evenly Round: 描述形状为圆形或球形的。例…

集群聊天项目

不懂的一些东西 (const TcpConnectionPtr&&#xff09;作为形参啥意思&#xff1a;接收一个常量引用&#xff0c;函数内部不允许修改该指针所指向的对象。 优势 1.网络层与业务层分离&#xff1a;通过网络层传来的id&#xff0c;设计一个map存储id以及对印的业务处理器&…

云计算基础-计算虚拟化-CPU虚拟化

CPU指令系统 在CPU的工作原理中&#xff0c;CPU有不同的指令集&#xff0c;如下图&#xff0c;CPU有4各指令集&#xff1a;Ring0-3&#xff0c;指令集是在服务器上运行的所有命令&#xff0c;最终都会在CPU上执行&#xff0c;但是CPU并不是说所有的命令都是一视同仁的&#xf…

vscode写MATLAB配置

vscode写MATLAB python下载 官网说明Versions of Python Compatible with MATLAB Products by Release - MATLAB & Simulink 不确定这三列都表示什么意思&#xff0c;尽量安装这三列都有的python版本吧&#xff0c;我安装的 MATLAB R2023b,python选择的是3.11.5 …

Midjourney绘图欣赏系列(四)

Midjourney介绍 Midjourney 是生成式人工智能的一个很好的例子&#xff0c;它根据文本提示创建图像。它与 Dall-E 和 Stable Diffusion 一起成为最流行的 AI 艺术创作工具之一。与竞争对手不同&#xff0c;Midjourney 是自筹资金且闭源的&#xff0c;因此确切了解其幕后内容尚不…

2.13:C语言测试题

21.(b) 6 22.(b) cd 23.b) 5 4 1 3 2 栈&#xff1a;先进后出 24. b,c,d:10,12,120 25.2,5 26.越界访问&#xff0c;可能正常输出&#xff0c;可能段错误 27. 0&#xff0c;41 28. a&#xff09;11 b) 320 29. aab; ba-b; aa-b; 30. p150x801005; p250x810…

TIM(Timer)定时中断 P1

难点&#xff1a;定时器级联、主从模式 一、简介&#xff1a; 1.TIM&#xff08;Timer&#xff09;定时器 定时器可以对输入的时钟进行计数&#xff0c;并在计数值达到设定值时触发中断 补充&#xff1a; { 定时器本质上是一个计数器&#xff0c;可以工作在定时或计数模式&…

哈希表哈希桶(C++实现)

哈希的概念 顺序结构以及平衡树中&#xff0c;元素关键码与其存储位置之间没有对应的关系&#xff0c;因此在查找一个元素 时&#xff0c;必须要经过关键码的多次比较。顺序查找时间复杂度为O(N)&#xff0c;平衡树中为树的高度&#xff0c;即 O( l o g 2 N log_2 N log2​N)&…

HTML-多媒体嵌入-MDN文档学习笔记

HTML-多媒体与嵌入 查看更多学习笔记&#xff1a;GitHub&#xff1a;LoveEmiliaForever MDN中文官网 HTML-中的图片 将图片放入网页 可以使用<img/>来将图片嵌入网页&#xff0c;它是一个空元素&#xff0c;最少只需src属性即可工作 <img src"图片链接"…

知识图谱:py2neo将csv文件导入neo4j

文章目录 安装py2neo创建节点-连线关系图导入csv文件删除重复节点并连接边 安装py2neo 安装python中的neo4j操作库&#xff1a;pip install py2neo 安装py2neo后我们可以使用其中的函数对neo4j进行操作。 图数据库Neo4j中最重要的就是结点和边&#xff08;关系&#xff09;&a…

【论文精读】BERT

摘要 以往的预训练语言表示应用于下游任务时的策略有基于特征和微调两种。其中基于特征的方法如ELMo使用基于上下文的预训练词嵌入拼接特定于任务的架构&#xff1b;基于微调的方法如GPT使用未标记的文本进行预训练&#xff0c;并针对有监督的下游任务进行微调。 但上述两种策略…

机器学习——聚类问题

&#x1f4d5;参考&#xff1a;西瓜书ysu老师课件博客&#xff08;3&#xff09;聚类算法之DBSCAN算法 - 知乎 (zhihu.com) 目录 1.聚类任务 2.聚类算法的实现 2.1 划分式聚类方法 2.1.1 k均值算法 k均值算法基本原理&#xff1a; k均值算法算法流程&#xff1a; 2.2 基于…

代码随想录算法训练营第31天|● 理论基础 ● 455.分发饼干 ● 376. 摆动序列 ● 53. 最大子序和

文章目录 理论基础分发饼干思路&#xff1a;代码&#xff1a; 摆动序列思路一 贪心算法&#xff1a;代码&#xff1a; 思路二&#xff1a;动态规划&#xff08;想不清楚&#xff09;代码&#xff1a; 最大子序和思路&#xff1a;代码&#xff1a; 理论基础 贪心算法其实就是没…
最新文章