用C语言进行学生成绩排序(插入排序算法)

news2024/9/29 21:22:00

一.排序算法

1.排序

从今天开始我们就要开始学习排序算法啦!

排序,就是重新排列表中的元素,使表中的元素满足按关键字有序的过程。为了查找方便,通常希望计算机中的表是按关键字有序的。

2.稳定性

除了我们之前了解的时间复杂度和空间复杂度来判断一个算法的好坏之外,在排序算法这里我们引入一个新的判断标准——稳定性。

算法的稳定性。若待排序表中有两个元素R;和R,其对应的关键字相同即keyi= keyj,且在排序前Ri在Rj的前面, 若使用某一排序算法排序后,Ri仍然在Rj的前面,则称这个排序算法是稳定的,否则称排序算法是不稳定的。需要注意的是,算法是否具有稳定性并不能衡量一个算法的优劣,它主要是对算法的性质进行描述。如果待排序表中的关键字不允许重复,则排序结果是唯一的,那么选择排序算法时的稳定与否就无关紧要。

3.算法分类

在这里插入图片描述

这里需要注意的是我们上一篇博客在图的应用中的拓扑排序他并不是我们这里严格意义上的排序算法。

二.直接插入排序

1.操作步骤

要将元素L(i)插入已有序的子序列[1……i-1],需要执行以下操作(为避免混淆,下面用L[ ]表示一个表,而用L()表示一个元素):

  • 1)查找出L(i)在L[…i-1]中的插入位置k。
  • 2)将L[.1i-1]中的所有元素依次后移-一个位置。
  • 3)将L(i)复制到L(k)。

为了实现对L1…n]的排序,可以将L(2) ~L (n)依次插入前面已排好序的子序列,初始L[1]可以视为是一个已排好序的子序列。上述操作执行n- 1次就能得到一个有序的表。插入排序在实现上通常采用就地排序(空间复杂度为0(1)),因而在从后向前的比较过程中,需要反复把已排序元素逐步向后挪位,为新元素提供插入空间。

2.举例演示

假定初始序列为49, 38, 65, 97 ,76, 13, 27,49,初始时49可以视为一个已排好序的子序列,按照上述算法进行直接插入排序的过程如图所示,括号内是已排好序的子序列。

在这里插入图片描述

3.性能分析

空间效率:仅使用了常数个辅助单元,因而空间复杂度为0(1)。

时间效率:在排序过程中,向有序子表中逐个地插入元素的操作进行了n-1趟,每趟操作都分为比较关键字和移动元素,而比较次数和移动次数取决于待排序表的初始状态。在最好情况下,表中元素已经有序,此时每插入一个元素,都只需比较一一次而不用移动元素,因而时间复杂度为0(n)。在最坏情况下,表中元素顺序刚好与排序结果中的元素顺序相反(逆序), 总的比较次数达到最大,总的移动次数也达到最大,总的时间复杂度为0(2)。平均情况下,考虑待排序表中元素是随机的,此时可以取上述最好与最坏情况的平均值作为平均情况下的时间复杂度,总的比较次数与总的移动次数均约为n2 /4。

4.代码实现

//直接插入排序
void InsertSort1(SqList &L){
	Elemtype temp;
	int i,j;
	for(i=1;i<L.length;i++){
		if(L.data[i].grade<L.data[i-1].grade){
			temp=L.data[i];
			for(j=i-1;j>=0 && temp.grade<L.data[j].grade;--j)    //从后往前查找待插入的位置
				L.data[j+1]=L.data[j];                 //向后挪位
			L.data[j+1]=temp;
		}
	}
}

三.折半插入排序

1.操作步骤

这里简要描述,详细请查看折半查找的博客线性表查找算法

从直接插入排序算法中,不难看出每趟插入的过程中都进行了两项工作:①从前面的有序子表中查找出待插入元素应该被插入的位置;②给插入位置腾出空间,将待插入元素复制到表中的插入位置。注意到在该算法中,总是边比较边移动元素。下面将比较和移动操作分离,即先折半查找出元素的待插入位置,然后统一地移动待插入位置之后的所有元素。当排序表为顺序表时,可以对直接插入排序算法做如下改进:由于是顺序存储的线性表,所以查找有序子表时可以用折半查找来实现。确定待插入位置后,就可统一-地向后移动元素。

2.性能分析

从上述算法中,不难看出折半插入排序仅减少了比较元素的次数,约为O(nlog2n),该比较次数与待排序表的初始状态无关,仅取决于表中的元素个数n;而元素的移动次数并未改变,它依赖于待排序表的初始状态。因此,折半插入排序的时间复杂度仍为O(n2), 但对于数据量不很大的排序表,折半插入排序往往能表现出很好的性能。折半插入排序是一种稳定的排序方法。

3.代码实现

//折半插入排序
void InsertSort2(SqList &L){
	Elemtype temp;
	int i, j, low, high, mid;
	for(i=1; i<L.length; i++){
		temp = L.data[i];
		low = 0; high = i-1;
		while(low <= high){ //折半查找
			mid = (low + high) / 2; //取中间点
			if(L.data[mid].grade > temp.grade)
				high = mid - 1;
			else
				low = mid + 1;
		}
		for(j=i-1; j>=high+1; --j){
			L.data[j+1] = L.data[j];
		}
		L.data[high+1] = temp;
	}
}

四.希尔排序

1.排序过程

希尔排序的过程如下:先取一个小于n的步长d,把表中的全部记录分成d组,所有距离为d1的倍数的记录放在同一组,在各组内进行直接插入排序;然后取第二个步长d2<d,重复上述过程,直到所取到的dt = 1,即所有记录已放在同一-组中, 再进行直接插入排序,由于此时已经具有较好的局部有序性,故可以很快得到最终结果。到目前为止,尚未求得一个最好的增量序列。

提出这个算法的本人提倡使用d=n/2

2.举例演示

在这里插入图片描述

3.性能分析

空间效率:仅使用了常数个辅助单元,因而空间复杂度为0(1)。

时间效率:由于希尔排序的时间复杂度依赖于增量序列的函数,这涉及数学上尚未解决的难题,所以其时间复杂度分析比较困难。当n在某个特定范围时,希尔排序的时间复杂度约为0(nl.3 )。在最坏情况下希尔排序的时间复杂度为0(n2 )。

稳定性:当相同关键字的记录被划分到不同的子表时,可能会改变它们之间的相对次序,因此希尔排序是一种不稳定的排序方法。

4.代码实现

//希尔排序
void ShellSort(SqList &L){
	int dk,i,j;
	Elemtype temp;
	for(dk=L.length/2;dk>=1;dk=dk/2){
		for(i=dk;i<L.length;++i){
			if(L.data[i].grade<L.data[i-dk].grade){
				temp=L.data[i];
				for(j=i-dk;j>=0 && temp.grade<L.data[j].grade;j-=dk)
					L.data[j+dk]=L.data[j];
				L.data[j+dk];
			}
		}
	}
}

五.C语言演示代码

/*我们今天的主角插入排序是基于查找算法来的,所以我们还是利用线性表来进行模拟*/

/*为了便于我们后面演示希尔排序,所以我们采用顺序存储结构*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define MaxSize 50                //这里只是演示,我们假设这里最多存五十个学生信息

//定义学生结构
typedef struct {
	char name[200];              //姓名
	int  grade;               //分数,这个是排序关键字
} Elemtype;

//声明使用顺序表
typedef struct {
	/*这里给数据分配内存,可以有静态和动态两种方式,这里采用动态分配*/
	Elemtype  *data;            //存放线性表中的元素是Elemtype所指代的学生结构体
	int length;                 //存放线性表的长度
} SqList;						//给这个顺序表起个名字,接下来给这个结构体定义方法

//初始化线性表
void InitList(SqList &L){
	/*动态分配内存的初始化*/
	L.data = (Elemtype*)malloc(MaxSize * sizeof(Elemtype));  //为顺序表分配空间
	L.length = 0;                                            //初始化长度为0
}

//求表长函数
int Length(SqList &L){
	return L.length;
}

//求某个数据元素值
bool GetElem(SqList &L, int i, Elemtype &e) {
	if (i < 1 || i > L.length)
		return false;         //参数i错误时,返回false
	e = L.data[i - 1];      //取元素值
	return true;
}

//输出线性表
void DispList(SqList &L) {
	if (L.length == 0)
		printf("线性表为空");
	//扫描顺序表,输出各元素
	for (int i = 0; i < L.length; i++) {
		printf("%s        %d", L.data[i].name,  L.data[i].grade);
		printf("\n");
	}
	printf("\n");
}

//插入数据元素
bool ListInsert(SqList &L, int i, Elemtype e) {
	/*在顺序表L的第i个位置上插入新元素e*/
	int j;
	//参数i不正确时,返回false
	if (i < 1 || i > L.length + 1 || L.length == MaxSize)
		return false;
	i--;                //将顺序表逻辑序号转化为物理序号
	//参数i正确时,将data[i]及后面的元素后移一个位置
	for (j = L.length; j > i; j--) {
		L.data[j] = L.data[j - 1];
	}
	L.data[i] = e;      //插入元素e
	L.length++;         //顺序表长度加1
	return true;
	/*平均时间复杂度为O(n)*/
}

//直接插入排序
void InsertSort1(SqList &L){
	Elemtype temp;
	int i,j;
	for(i=1;i<L.length;i++){
		if(L.data[i].grade<L.data[i-1].grade){
			temp=L.data[i];
			for(j=i-1;j>=0 && temp.grade<L.data[j].grade;--j)    //从后往前查找待插入的位置
				L.data[j+1]=L.data[j];                 //向后挪位
			L.data[j+1]=temp;
		}
	}
}
/*还记得我们之前学的查找算法吗,其实就是线性查找和折半查找到需要插入元素的位置*/

//折半插入排序
void InsertSort2(SqList &L){
	Elemtype temp;
	int i, j, low, high, mid;
	for(i=1; i<L.length; i++){
		temp = L.data[i];
		low = 0; high = i-1;
		while(low <= high){ //折半查找
			mid = (low + high) / 2; //取中间点
			if(L.data[mid].grade > temp.grade)
				high = mid - 1;
			else
				low = mid + 1;
		}
		for(j=i-1; j>=high+1; --j){
			L.data[j+1] = L.data[j];
		}
		L.data[high+1] = temp;
	}
}

//希尔排序
void ShellSort(SqList &L){
	int dk,i,j;
	Elemtype temp;
	for(dk=L.length/2;dk>=1;dk=dk/2){
		for(i=dk;i<L.length;++i){
			if(L.data[i].grade<L.data[i-dk].grade){
				temp=L.data[i];
				for(j=i-dk;j>=0 && temp.grade<L.data[j].grade;j-=dk)
					L.data[j+dk]=L.data[j];
				L.data[j+dk]=temp;
			}
		}
	}
}

int main(){
	SqList L;
	Elemtype stuents[10]={{"张三",649},{"李四",638},{"王五",665},{"赵六",697},{"冯七",676},
		{"读者",713},{"阿强",627},{"杨曦",649},{"老六",655},{"阿黄",604}};
	//这一部分忘了的请回顾我的相关博客
	printf("初始化顺序表并插入开始元素:\n");
	InitList(L);         //这时是一个空表,接下来通过插入元素函数完成初始化
	for (int i = 0; i < 10; i++)
		ListInsert(L, i + 1, stuents[i]);
	DispList(L);
	printf("根据分数进行直接插入排序后:\n");
	InsertSort1(L);
	DispList(L);
	/*
	  printf("根据分数进行折半插入排序后:\n");
	  InsertSort2(L);
	  DispList(L);
	  printf("根据分数进行希尔排序后:\n");
	  ShellSort(L);
	  DispList(L);
	 */
	//请确保这里在进行一次排序后,不要在用其他方法排序,没有意义,可以重新初始化线性表,再换一种方法排序
}





这里请注意,设计线性表的相关问题,请看顺序表算法练习

六.运行结果

1.直接插入排序

在这里插入图片描述

2.折半插入排序

在这里插入图片描述

3.希尔排序

在这里插入图片描述

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

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

相关文章

基于springboot+Redis的前后端分离项目之分布式锁(四)-【黑马点评】

&#x1f381;&#x1f381;资源文件分享 链接&#xff1a;https://pan.baidu.com/s/1189u6u4icQYHg_9_7ovWmA?pwdeh11 提取码&#xff1a;eh11 分布式锁 分布式锁1 、基本原理和实现方式对比2 、Redis分布式锁的实现核心思路3 、实现分布式锁版本一4 、Redis分布式锁误删情况…

S3版本控制,复制和生命周期配置

Hello大家好&#xff61; 在本课时我们将讨论S3的三个功能特性&#xff0c;这三个特性有一些相关性&#xff0c;即版本控制&#xff0c;复制和生命周期配置。 S3版本控制 首先版本控制&#xff0c;是将对象的多个版本保存在同一存储桶的方法。换句话说&#xff0c;您上传一个对…

数据结构--顺序表的查找

数据结构–顺序表的查找 顺序表的按位查找 目标&#xff1a; GetElem(L,i):按位查找操作。获取表L中第i个位置的元素的值。 代码实现 #define MaxSize 10 typedef struct {ElemType data[MaxSize];int len; }Sqlist;ElemType GetElem(Sqlist L, int i) {return L.data[i-1]…

海外问卷调查项目可靠吗?是违法的吗?

可靠。 最近&#xff0c;一个备受瞩目的创业项目在社会上引起了广泛关注&#xff0c;这个项目集创业、全职和兼职于一体&#xff0c;被称为"海外问卷调查项目"&#xff0c;成为了无数人追逐的新选择。 然而&#xff0c;自中美贸易摩擦以来&#xff0c;中国人对&quo…

使用CloudOS快速实现K8S容器化部署

关于容器技术 容器技术&#xff08;以docker和Kubernetes为代表&#xff09;呱呱坠地到如今&#xff0c;在国内经历了如下3个阶段&#xff1a; 婴儿期&#xff1a;2014-2016年的技术探索期&#xff1b; 少儿期&#xff1a;2017-2018年的行业试水期&#xff1b; 少年期&…

1.设计模式之七大原则和介绍

0.为什么我要学习设计模式呢? 我发现mysql的jdbc有factory有工厂模式(编程思想,不指定语言都可以用) mq有一个QueueBuilder().setArg().xxx().build建造者模式,单例模式貌似也遇到过,前端也遇到了好几个设计模式的问题,比如prototype深拷贝和浅拷贝 所以我决定系统的学习一下设…

TC8:SOMEIP_ETS_004-005

SOMEIP_ETS_004: Burst_Test 目的 检查DUT是否可以在短时间内处理突发请求并返回所有请求的响应 测试步骤 Tester:新建有效SOME/IP消息Tester:使用method echoUINT8发送突发SOME/IP Request消息DUT:返回每个请求消息的响应消息期望结果 3、DUT:返回每个请求消息的响应消息…

学redis这一篇就够了

目录 1.下载安装启动 1.1 临时启动服务 2.2 默认服务安装 2.常用五大基本数据类型 2.1 key操作 2.2 字符串&#xff08;String&#xff09; 2.3 列表&#xff08;List&#xff09; 2.4 Set&#xff08;集合&#xff09; 2.5 Hash&#xff08;哈希&#xff09; 2.6 Zs…

分离表示学习:通用图像融合框架

IFSepR: A General Framework for Image Fusion Based on Separate Representation Learning &#xff08;IFSepR&#xff1a;一种基于分离表示学习的通用图像融合框架&#xff09; 提出了一种基于分离表示学习的图像融合框架IFSepR。我们认为&#xff0c;基于先验知识的共模…

Fast Segment Anything Model(FastSAM)

Fast Segment Anything Model&#xff08;FastSAM&#xff09; Fast Segment Anything Model&#xff08;FastSAM&#xff09;是一个仅使用SAM作者发布的SA-1B数据集的2%进行训练的CNN Segment Anything模型。FastSAM在50倍的运行速度下实现了与SAM方法相当的性能。 SAM代码&a…

pubg 依赖安装

一、安装python 1、进入官网 https://www.python.org/ 2、勾选Add python.exe to PTHA 3、自定义下载 测试和文档不需要勾选&#xff0c;然后next 4、自定义安装路径 点击install安装 安装成功&#xff0c;点击close。 5、测试 windr键&#xff0c;输入cmd 输入python回…

基于SSM的餐厅点餐系统设计与实现(Java+MySQL)

目 录 第一章 绪论 1 1.1系统研究背景和意义 1 1.2研究现状 1 1.3论文结构 2 第二章 相关技术说明 3 2.1 JSP(Java Server Page)简介 3 2.2 Spring框架简介 4 2.3 Spring MVC框架简介 5 2.4 MyBatis 框架简介 5 2.4 MySql数据库简介 5 2.6 Tomcat简介 6 2.7 jQuery简介 7 2.8系…

计算机毕业论文内容参考|基于大数据的信息物理融合系统的分析与设计方法

文章目录 导文摘要前言绪论课题背景国内外现状与趋势:课题内容:相关技术与方法介绍:系统架构设计:数据采集与处理:数据存储与管理:数据分析与挖掘:系统优化与调试:应用场景:挑战与机遇:研究方向:系统分析:系统设计:系统实现:系统测试:总结与展望:

SpringBoot原理(1)--@SpringBootApplication注解使用和原理/SpringBoot的自动配置原理详解

文章目录 前言主启动类的配置SpringBootConfiguration注解验证启动类是否被注入到spring容器中 ComponentScan 注解ComponentScan 注解解析与路径扫描 EnableAutoConfiguration注解 问题解答1.AutoConfigurationPackage和ComponentScan的作用是否冲突起因回答 2.为什么能实现自…

WIN10上必不可少的5款优质软件

噔噔噔噔&#xff0c;作为一个黑科技软件爱好者&#xff0c;电脑里肯定是不会缺少这方面的东西&#xff0c;今天的5款优质软件闪亮登场了。 颜色拾取器——ColorPix ​ ColorPix是一个颜色拾取器工具&#xff0c;可以让你快速地获取屏幕上任意位置的颜色值&#xff0c;如RGB、…

ivshmem-plain设备原理分析

文章目录 前言基本原理共享内存协议规范 具体实现设备模型数据结构设备初始化 测试验证方案流程Libvirt配置Qemu配置测试步骤 前言 ivshmem-plain设备是Qemu提供的一种特殊设备&#xff0c;通过这个设备&#xff0c;可以实现虚机内存和主机上其它进程共存共享&#xff0c;应用…

618美妆个护28个榜单:欧莱雅稳住冠军?珀莱雅大爆发第二?

存量时代的购物造节大竞争&#xff0c;作为消费复苏后的首场大促&#xff0c;今年的618堪称史上最“卷”&#xff0c;也承载着消费振兴、经济复苏等希望。 不过&#xff0c;今年所有平台都未公布具体GMV&#xff0c;某种程度说明大促造节的时代俨然已成过去式了。 5月18日&am…

怎么去除视频里的背景音乐?其实非常简单!

如何去除视频背景音乐&#xff1f;在视频处理中&#xff0c;有时我们需要从视频中提取声音并进行处理&#xff0c;而不仅仅是简单地去除整个背景音乐。我们可能需要有选择性地去除人声或背景音乐。这个处理过程对于选用合适的工具至关重要。在本文中&#xff0c;我将分享两种可…

【⑦MySQL】· 一文了解四大子查询

前言 ✨欢迎来到小K的MySQL专栏&#xff0c;本节将为大家带来MySQL标量/单行子查询、列子/表子查询的讲解✨ 目录 前言一、子查询概念二、标量/单行子查询、列子/表子查询三、总结 一、子查询概念 子查询指一个查询语句嵌套在另一个查询语句内部的查询&#xff0c;这个特性从My…

抖音林客生活服务商机构

抖音林客生活服务商机构是在抖音平台上提供各种生活服务的机构或组织。这些机构通常会提供家政服务、保洁服务、美容美发服务等&#xff0c;也有一些提供餐饮、旅游、电商等服务。用户可以通过抖音搜索、浏览和下单&#xff0c;享受到优质的服务体验。 这些服务商机构在抖音…