c语言数据结构(9)——插入排序、希尔排序

欢迎来到博主的专栏——C语言数据结构
博主ID:代码小豪

文章目录

    • 排序
    • 插入排序
    • 希尔排序

排序

现在有N个数据的序列,其对应的序列号为[r1 ,r2 ……rn];将该序列对应的数据[k1 ,k2 ……kn]排成满足递减或递减的序列的操作称为排序

插入排序

玩过斗地主的小伙伴都有过这种经历吧,拿到的牌是一些无序的牌组,这时候很难发现卡牌之间的组合,比如顺子、飞机。如果我们将牌顺好之后,牌与牌之间的关系就变得显而易见了。(顺牌:将扑克牌的点数排成升序或者降序)。

不知道大伙顺牌的方式是不是和我一样,比如一个点数为7的牌在大王的后面,我就将这个7插入与7最接近的两个点数的扑克牌之间。
在这里插入图片描述
这个过程是怎样的呢?首先比较7和大王,7比大王小,向后比较,比较7和J,7比J小,向后比较,最后比较7和5, 7比5大,将7插入到5的后面。

细心观察可以发现,7之间的扑克牌已经升序排列了,这和平时打牌的时候一样,将7插入到5与J之间的前提是不是已经将5与J排好了才能插入?对于人来说,这是一个很简单的操作,因为人脑可以随机应变的将5与J拍好序,但是对于机器来说,它是需要一个固定的操作来完成的。我们需要一个稳定的逻辑来实现这种插入排序。

还是回到扑克牌,现在将这个牌型变得更加无序。
在这里插入图片描述
现在该如何用插入排序将这堆牌排成升序呢?

前面将7进行插入排序的时候,需要让7之间的扑克牌点数处于升序状态,从这里可以提炼出一个信息。

(1)若是想要用插入排序将一组数据排成升序,就要先将这个数据的前边数据排成升序
(2)结束插入排序后,这组数据会构成升序。

假如现在有N个数据,那么这N个数据用插入排序算法的思路就应该如下:

(1)为了让n能使用插入排序,将前n-1个数据排成升序,这样就能让n个数据排成升序
(2)为了让n-1个数据能构成升序,对n-1进行插入排序,这就需要先让前n-2个数据排成升序
……
(N-1)为了让前2个数据排成升序,就需要对第2个数据使用插入排序,此时就不存在前一个数据不符合升序的问题了。第一个数据无论如何都满足升序的要求。

从这里可以推断出,如果从第二个数据开始使用插入排序,一直到第N个数据,那么这堆数据就可以完成排序。

在这里插入图片描述

回到扑克牌,我们需要从第二张牌开始进行插入排序
在这里插入图片描述
7比大王小,继续往前比较,但是此时前面不在有点数了,于是将7放在大王的前面在这里插入图片描述
5比大王小,继续向前比较,5又比7小,将5插入至7的前面。
在这里插入图片描述
j比大王小,继续向前比较,j比7大,插入至7的后面。

在这里插入图片描述
此时就完成了4个扑克牌的排序。

插入排序的代码如下:

void InsertSort(int* a, int n)//a是待排序数组,n是数组的元素个数
{
	int end = 0;
	for (int i = 1; i < n ; i++)
	{
		end = i;//从第2个数据开始插入排序,一直到第n个数,构成排序
		int tmp = a[end];//将进行插入排序的数据进行保存,方便找到位置后进行插入
		while (end > 0)
		{
			if (tmp < a[end - 1])
			{
				a[end] = a[end - 1];//将不符合条件的数据向后移动一位,空出空间。
				end--;//继续往前比较
			}
			else//找到适合的位置就退出循环,此时end位于合适的位置
			{
				break;
			}
		}
		a[end] = tmp;
	}
}

希尔排序

希尔排序是插入排序的改进版本,由Shell提出的一种排序算法。插入排序在某种情况下会变得高效,比如数据本就升序或者接近升序的状态,此时插入排序的时间复杂度接近O(N),而非常规情况下的O(N2)。

希尔排序就是利用了这个特点,如果我们可以在进行插入排序之前,先将无序的数据排列的接近有序,这样子插入排序的效率就会变得更高。

如何将数据变得接近有序呢?希尔排序中采用了一种预排序的方法,通过预排序可以简单的想将数据排列成接近有序的序列。预排序的思路如下:

(1)将整个序列分成多个子序列
(2)将子序列的的数据排成有序

那么问题就在于如何分割子序列了:现在有这么一堆无序数据: { 5,3,4,6,7,9,1,8,2 };我们简单将该序列分成3组{5,3,4},{6,7,9},{1,8,2}。进行预排序后的子序列变成{3,4,5},{6,7,9},{1,2,8},合并子序列后的数据变成{3,4,5,6,7,9,1,2,8},这个序列显然是不符合接近有序这个要求的。

想要序列变成接近有序,就需要让大的数据排在后面,小的数据排在前面,这样子的预排序才是有效的,于是希尔想出的预排序方法如下:
子序列不再是相邻的,而是跳跃的.,让相距某个增量的数据组成子序列。
在这里插入图片描述
将子序列进行排序后的序列为
在这里插入图片描述
可以发现这个序列比预排序之前的序列更加有序。如果再对这个序列进行增量更小的预排序,这个序列将会更加更近有序。

那么如何选择预排序的增量呢?常用的方法有两个:一个是让增量等于前一次预排序的增量的一半,另外则是让增量等于前一次预排序的增量的3分之1再加1。但是无论如何,最后一次预排序的增量必须为1,这样才能对序列进行完全的排序。

前面只提到了预排序却没提到预排序的排序方法是什么,实际上预排序采用的排序算法是插入排序的变种之一。插入排序是比较前一个数据,而希尔排序中的预排序是比较前增量个数据。
在这里插入图片描述
每完成一趟预排序,序列更加接近有序。与此同时逐渐减少预排序的增量,让预排序能让序列更加有序。最后一趟希尔排序增量一定要为1,此时整个序列变得有序。

这里先给出希尔排序的程序,再图解过程

void ShellSort(int* a, int n)//n是数组的元素个数
{
	int gap = n;//增量为gap
	int tmp = 0;
	while (gap > 1)
	{
		gap /= 2;//一趟希尔排序后增量变化,保证最后一趟希尔排序的增量为1.
		//也可以写成,gap=gap/3+1,各有优劣
		for (int i = 0; i < n - gap; i++)//一趟预排序
		{
			int end = i;
			while (end >= 0)
			{
				tmp = a[end + gap];
				if (a[end] > a[end + gap])
				{
					a[end + gap] = a[end];//将预排的子序列进行排序
					end -= gap;
				}
				else
				{
					break;
				}
				a[end + gap] = tmp;//插入合适的数据位置
			}
		}
	}
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
可以发现经过两趟排序之后,序列基本有序
在这里插入图片描述
我们对比一下增量为1的希尔排序和插入排序的代码
插入排序的代码:

for (int i = 1; i < n ; i++)
{
	end = i;//从第2个数据开始插入排序,一直到第n个数,构成排序
	int tmp = a[end];//将进行插入排序的数据进行保存,方便找到位置后进行插入
	while (end > 0)
	{
		if (tmp < a[end - 1])
		{
			a[end] = a[end - 1];//将不符合条件的数据向后移动一位,空出空间。
			end--;//继续往前比较
		}
		else//找到适合的位置就退出循环,此时end位于合适的位置
		{
			break;
		}
	}
	a[end] = tmp;
}

增量为1的希尔排序的代码

for (int i = 0; i < n - gap; i++)//一趟预排序
{
	int end = i;
	while (end >= 0)
	{
		tmp = a[end + gap];
		if (a[end] > a[end + gap])
		{
			a[end + gap] = a[end];//将预排的子序列进行排序
			end -= gap;
		}
		else
		{
			break;
		}
		a[end + gap] = tmp;//插入合适的数据位置
	}
}

将希尔排序的增量(gap)改为1,可以发现与插入排序的代码逻辑无异。

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

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

相关文章

tomcat配置静态资源后无法正常访问

目录 一、场景二、配置三、访问异常四、排查五、原因六、解决 一、场景 1、将前端文件存在到指定目录 2、在tomcat配置静态资源 3、配置后无法正常访问到前端文件 二、配置 1、tomcat配置 2、静态资源 三、访问异常 四、排查 可以ping通&#xff0c;但是访问不了3080端口 …

探究WordPress受欢迎的原因及其org和com的区别

在当今互联网时代&#xff0c;WordPress已经成为了建立网站的首选工具之一&#xff0c;其受欢迎程度远远超出了其他竞争对手。那么&#xff0c;为什么WordPress如此受欢迎呢&#xff1f;让我们一起探究一下。 首先&#xff0c;WordPress是一个开源项目&#xff0c;这意味着任何…

【UEditorPlus】后端配置项没有正常加载,上传插件不能正常使用

解决办法&#xff1a; 1、找到UEditorPlus的根目录&#xff0c;修改 ueditor.all.js 文件 搜索&#xff1a;isJsonp utils.isCrossDomainUrl(configUrl); 更改为&#xff1a;isJsonp false; 2、重新运行前端即可正常使用 如果出现依旧不行&#xff0c;请关闭服务&#xff…

如何选择适合自己的办公空间

说到办公地点的选择&#xff0c;其实就跟挑衣服似的&#xff0c;得看场合、看需求&#xff0c;还得看个人喜好。有的人喜欢自由自在&#xff0c;有的人则需要稳定和私密。所以&#xff0c;咱们来看看哪些朋友更适合哪种办公环境。 适合共享办公室的&#xff1a; 刚起步的小公司…

教师的晋升通道:走向专业成长的阶梯

教师是一项需要不断学习、不断进步的职业。随着教育改革的不断深入&#xff0c;教师的晋升通道也越来越受到关注。本文将从教师的晋升通道、晋升标准和未来发展方向等方面进行探讨&#xff0c;旨在帮助广大教师了解自己的职业成长路径&#xff0c;促进个人发展。 一、教师的晋升…

rtph264depay插件分析笔记

1、rtp协议头 2、rtp可以基于TCP或者UDP 其中基于TCP需要加4个字节的RTP标志 3、rtph264depay定义解析函数gst_rtp_h264_depay_process&#xff0c;通过RFC 3984文档实现。 static void gst_rtp_h264_depay_class_init (GstRtpH264DepayClass * klass) {GObjectClass *gobject…

RTSP应用:实现视频流的实时推送

在实现实时视频流推送的项目中&#xff0c;RTSP&#xff08;Real Time Streaming Protocol&#xff09;协议扮演着核心角色。本文将指导你通过安装FFmpeg软件&#xff0c;下载并编译live555&#xff0c;以及配置ffmpeg进行视频流推送&#xff0c;来实现一个基本的RTSP流媒体服务…

WIN使用LPD协议来共享打印机含统信UOS

打开“控制面板”&#xff0c;“程序和功能”&#xff0c;“启动或关闭Windows功能”&#xff0c;下拉找到“打印和文件服务”&#xff0c;勾选“LPD打印服务”和“LPR端口监视器”。确定之后重启电脑&#xff0c;共享主机和其它需要添加共享打印机的都开启功能和重启。 一、启…

SpringMVC设置全局异常处理器

文章目录 背景分析使用ControllerAdvice&#xff08;RestControllerAdvice&#xff09;ExceptionHandler实现全局异常全局异常处理-多个处理器匹配顺序存在一个类中存在不同的类中 对于过滤器和拦截器中的异常&#xff0c;有两种思路可以考虑 背景 在项目中我们有需求做一个全…

定时器的原理和应用

#include<reg51.h> unsigned char s[]{0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F}; unsigned char count0,num0; void inittimer() {TMOD0x01;//0000 0001TH0(65536-50000)/256; //定时50ms50000us 2562^8 初值向右边移动8位TL0(65536-50000)%256;ET01;//开启定…

TouchGFX之Button

TouchGFX中的按钮是一种感应触控事件的控件&#xff0c;能够在按钮被按下/释放时发送回调 代码 #ifndef TOUCHGFX_ABSTRACTBUTTON_HPP #define TOUCHGFX_ABSTRACTBUTTON_HPP #include <touchgfx/Callback.hpp> #include <touchgfx/events/ClickEvent.hpp> #includ…

面试题目--3.19

1.foo()和foo()之间有什么区别&#xff1f; 代表所有的warning忽略 2.什么是csrf攻击&#xff1f;如何防范&#xff1f; csrf&#xff0c;跨站请求伪造&#xff0c;攻击方伪装用户身份发送请求从而窃取信息或者破坏系统。 基本原理&#xff1a;用户访问a网站登录并生成了coo…

opencv 十九 python下实现多线程间rtsp直播流的复用

在多线程拉流的任务场景中&#xff0c;有时需要将一个rtsp拉取多次&#xff0c;每重新打开一次rtsp视频流就要多消耗一次带宽&#xff0c;为此基于类的静态对象实现rtsp视频流的复用。 1、实现代码 import threading import cv2,time #接收摄影机串流影像&#xff0c;采用多线…

论文《Exploring to Prompt for Vision-Language Models》阅读

论文《Exploring to Prompt for Vision-Language Models》阅读 论文概况论文动机&#xff08;Intro&#xff09;MethodologyPreliminaryCoOp[CLASS]位置Context 是否跨 class 共享表示和训练 ExperimentsOverall ComparisonDomain GeneralizationContext Length (M) 和 backbon…

如何配置本地ssh连接远程Linux服务器

1.条件 本地操作系统Ubuntu远程服务器&#xff08;Linux都可以&#xff09; 本地如果是Window,其实也一样&#xff0c;但是需要先下载ssh和putty工具&#xff0c;然后操作步骤是一样的 2.生成ssh公私钥对 # 在本地重新生成SSH公私钥对非常简单&#xff0c;在你的命令行终端&a…

vscode从安装到卸载

&#x1f308;个人主页&#xff1a;Rookie Maker &#x1f3c6;&#x1f3c6;关注博主&#xff0c;随时获取更多关于IT的优质内容&#xff01;&#x1f3c6;&#x1f3c6; &#x1f600;欢迎来到小田代码世界~ &#x1f601; 喜欢的小伙伴记得一键三连哦 ૮(˶ᵔ ᵕ ᵔ˶)ა …

任务2.1 一元二次方程(顺序结构版)

在这个任务中&#xff0c;我们编写了一个Java程序来解决一元二次方程。程序接受用户输入的系数a、b、c&#xff0c;并计算出方程的根。通过计算判别式delta的值&#xff0c;我们可以确定方程有两个不相等实根、两个相等实根还是没有实数根。这个程序遵循了IPO模式&#xff0c;即…

GEC6818开机自动加载驱动与更改开发板的RTC时钟

GEC6818开机自动加载驱动与更改开发板的RTC时钟 本文主要涉及&#xff1a; 1.GEC6818开机自动加载驱动 2.更改开发板的RTC时钟 文章目录 GEC6818开机自动加载驱动与更改开发板的RTC时钟一、开机自动加载驱动或运行程序**STEP1&#xff1a;** 使用vi打开文件profile.命令如下**S…

【 MyBatis 】| 关于多表联查返回 List 集合只查到一条的 BUG

目录 一. &#x1f981; 写在前面二. &#x1f981; 探索过程2.1 开端 —— 开始写 bug2.2 发展 —— bug 完成2.3 高潮 —— bug探究2.4 结局 —— 效果展示 三. &#x1f981; 写在最后 一. &#x1f981; 写在前面 今天又是 BUG 气满满的一天&#xff0c;一个 xxxMapper.xm…

linux网络服务学习(4):SAMBA

1.什么是SAMBA SAMBA也是一种文件共享工具 &#xff08;1&#xff09;服务名&#xff1a;smb &#xff08;2&#xff09;软件名&#xff1a;samba &#xff08;3&#xff09;配置文件&#xff1a; /etc/samba/smb.conf /etc/samba/smb.conf.example &#xff08;4&#…
最新文章