【数据结构】第十五弹---C语言实现直接插入排序

news2024/11/16 3:29:23

 

个人主页: 熬夜学编程的小林

💗系列专栏: 【C语言详解】 【数据结构详解】【C++详解】

目录

1、排序的概念及其运用

1.1、排序的概念与分类

1.2、排序运用

1.3、常见的排序算法

1.4、常见的排序算法性能测试

2、常见排序算法的实现

2.1、直接插入排序

2.1.1、基本思想

2.1.2、代码实现

2.1.3、代码测试

2.1.4、时空复杂度分析

总结


1、排序的概念及其运用


1.1、排序的概念与分类


排序:

排序是一种将一组对象按照某种特定顺序重新排列的过程。在计算机科学中,排序是数据处理中非常基本且重要的操作,它可以帮助人们更有效地理解和分析数据。排序的顺序通常是升序或降序,也可以按照数字、字母、大小或其他标准进行排序。


稳定性:

假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i] == r[j],且 r[i] 在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。


内部排序:

数据元素全部放在内存中的排序。


外部排序:

数据元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的排序。

1.2、排序运用

排序的运用非常常见,在我们购物的软件上面,经常会根据不同的属性来进行排序,我们以前上学时候的成绩通常也会对各科进行排序!!!!


1.3、常见的排序算法

常见的排序算法有冒泡排序、选择排序、插入排序、快速排序、希尔排序、堆排序、归并排序等等!!!

注意:在本专栏中,还会讲解计数排序,它的效率非常高,但是局限性也很大。 

如下为常见排序实现的接口:

// 排序实现的接口

// 插入排序
void InsertSort(int* a, int n);
// 希尔排序
void ShellSort(int* a, int n);
// 选择排序
void SelectSort(int* a, int n);
// 向下调整算法
void AdjustDwon(int* a, int n, int root);
// 堆排序
void HeapSort(int* a, int n);
// 冒泡排序
void BubbleSort(int* a, int n)
// 快速排序递归实现
// 快速排序hoare版本
int PartSort1(int* a, int left, int right);
// 快速排序挖坑法
int PartSort2(int* a, int left, int right);
// 快速排序前后指针法
int PartSort3(int* a, int left, int right);
void QuickSort(int* a, int left, int right);
// 快速排序 非递归实现
void QuickSortNonR(int* a, int left, int right)
// 归并排序递归实现
void MergeSort(int* a, int n)
// 归并排序非递归实现
void MergeSortNonR(int* a, int n)
// 计数排序
void CountSort(int* a, int n)

1.4、常见的排序算法性能测试

实现好排序之后必不可少要对效率进行测试,前面我们用到的都是时间空间复杂度来评价一个算法的好坏,此处我们再用实际的时间来证明排序的性能!!!

// 测试排序的性能对比
void TestOP()
{
	srand(time(0));// 生成随机数的种子
	const int N = 10000000;// 动态开辟空间的元素个数,开辟较大的空间适用于效率高的排序
	int* a1 = (int*)malloc(sizeof(int) * N);
	int* a2 = (int*)malloc(sizeof(int) * N);
	int* a3 = (int*)malloc(sizeof(int) * N);
	int* a4 = (int*)malloc(sizeof(int) * N);
	int* a5 = (int*)malloc(sizeof(int) * N);
	int* a6 = (int*)malloc(sizeof(int) * N);
	int* a7 = (int*)malloc(sizeof(int) * N);
	int* a8 = (int*)malloc(sizeof(int) * N);
	for (int i = 0; i < N; ++i)
	{
		a1[i] = rand() + i;// rand()随机数只有几万个,不能达到真正的随机,加上i让它更随机
		a2[i] = a1[i];
		a3[i] = a1[i];
		a4[i] = a1[i];
		a5[i] = a1[i];
		a6[i] = a1[i];
		a7[i] = a1[i];
		a8[i] = a1[i];
	}
	//clock计算程序运行到此时的时间 毫秒
	int begin1 = clock();
	InsertSort(a1, N);
	int end1 = clock();

	int begin2 = clock();
	ShellSort(a2, N);
	int end2 = clock();

	int begin3 = clock();
	SelectSort(a3, N);
	int end3 = clock();

	int begin4 = clock();
	HeapSort(a4, N);
	int end4 = clock();

	int begin5 = clock();
	QuickSort(a5, 0, N - 1);
	int end5 = clock();

	int begin6 = clock();
	MergeSort(a6, N);
	int end6 = clock();

	int begin7 = clock();
	BubbleSort(a7, N);
	int end7 = clock();

	int begin8 = clock();
	CountSort(a8, N);
	int end8 = clock();
    
    // 计算排序所用时间
	printf("InsertSort:%d\n", end1 - begin1);
	printf("ShellSort:%d\n", end2 - begin2);
	printf("SelectSort:%d\n", end3 - begin3);
	printf("HeapSort:%d\n", end4 - begin4);
	printf("QuickSort:%d\n", end5 - begin5);
	printf("MergeSort:%d\n", end6 - begin6);
	printf("BubbleSort:%d\n", end7 - begin7);
	printf("CountSort:%d\n", end8 - begin8);

    // 释放空间
	free(a1);
	free(a2);
	free(a3);
	free(a4);
	free(a5);
	free(a6);
	free(a7);
	free(a8);
}

 排序OJ(可使用各种排序跑这个OJ)   :

排序OJicon-default.png?t=N7T8https://leetcode.cn/problems/sort-an-array/description/

2、常见排序算法的实现


2.1、直接插入排序


2.1.1、基本思想


直接插入排序是一种简单的插入排序法,其基本思想是:

把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列 。实际中我们玩扑克牌时,就用了插入排序的思想。


实现思路:

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

直接插入排序动图如下:


2.1.2、代码实现

第一步:创建函数,确定函数的形参,毋庸置疑的是需要传一个待排序的数组,其次进行排序的时候是需要遍历数组的,因此需要知道元素个数。为什么不能再函数内部计算元素个数呢???

答案是将数组作为形参传给函数,此数组的实质是一个地址,而计算大小是用数组大小/数组元素大小,此时sizeof(数组名) 计算的大小是4或者8,不能准确计算元素个数。

因此函数为:

void InsertSort(int a[], int n);//升序

第二步:分析单趟排序的情况

  情况一:待排序的数大于其中任意一个已经排序好的数( tmp > a[i] )。

默认0 - end区间是有序的,将end + 1位置的数插入到有序数组中。

  • 这里的end代表已排序序列的最后一个元素的索引。将插入的数(tmp)依次往前比较,如果比前面的end位置的数小,则将此end位置的数放到end + 1 位置,并将end --。
  • 如果大于等于前面的数字,说明此位置就是插入的数的位置
  • 将插入的数给end + 1下标即可。

  情况二:待排序的数比所有已经排序好的数要小。

通过上面两种情况可以总结出循环的条件是end >=0。

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

这里跳出循环有两种情况:

1.找到插入位置:当tmp大于等于a[end],这表明tmp应该插入在a[end]的后面,因为在tmp之前的元素(包括a[end]自己)都已经排序好了,并且都是小于等于tmp的。这就是tmp的正确位置,在这种情况下,我们执行break语句跳出循环,并将tmp放置在end + 1的位置。


2.达到有序序列的起点:当循环保持进行,end值在每次迭代中不断递减,如果tmp小于所有已排序的元素,end可能会变成-1,这意味着tmp比有序序列中所有现有的元素都要小,应该被放在整个序列的最开始位置。


在这两种跳出循环的情况下,我们总是需要执行a[end + 1] = tmp;来将tmp元素放置到正确的位置上。因为无论是找到合适的插入点还是tmp成为新的最小元素,我们都需要将它实际插入到有序序列中,这就是为什么这行代码放在循环之外,确保跳出循环后,我们执行最终的插入动作。

第三步:封装整个排序

一个元素就是有序数组,因此end从0开始。

// 升序
void InsertSort(int a[], int n)
{
	// [a,end]有序
	for (int i = 0; i < n - 1; i++)
	{
		int end = i;
		int tmp = a[end + 1];
		while (end >= 0)
		{
			if (a[end] > tmp) // 前面的数大于后面则覆盖前面
			{
				a[end + 1] = a[end];
				end--;
			}
			else
			{
				break;
			}
		}
		a[end + 1] = tmp;// 填充空位置
	}
}

2.1.3、代码测试

按照我们正常的惯例,实现完之后都需要进行测试,此处有两种测试方式,其一为打印测试,其二为调试测试,我们现在实现的都是排序算法,因此可以直接封装一个打印数组函数进行测试。

 打印函数如下:

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

测试代码:

int main()
{
	int a[] = { 9,8,7,6,5,4,3,2,1,0 };//给一组数据
	int sz = sizeof(a) / sizeof(a[0]);//计算数组元素个数
	printf("排序前:\n");
	ArrayPrint(a, sz);
	InsertSort(a, sz);
	printf("排序后:\n");
	ArrayPrint(a, sz);
	return 0;
}

测试结果: 

 

2.1.4、时空复杂度分析

时间复杂度:

插入排序算法的时间复杂度取决于输入数组中元素的初始排序状态

最坏情况 :如果数组是完全逆序的,那么每次插入操作都需要将元素移到已排序部分的开头。这就意味着对于第i个元素,可能需要进行i次比较和移动。这种情况下,算法的时间复杂度是O(N2),因为需要进行总计约1 + 2 + 3 + … + (n-1)次比较,这是一个n(n-1)/2的等差数列

最好情况 :这种情况发生在数组已经完全有序时。在这种情况下,每次比较后,很快就会找到插入位置(在已排序元素的末尾),不需要进行额外的移动。因此,最好情况下插入排序的时间复杂度是O(N),因为外层循环只会遍历一次数组,内层循环不会进行任何实际的比较和移动操作。

我们时间复杂度分析看的是最坏情况,因此直接插入排序的时间为O(N^2)。

空间复杂度:

插入排序的空间复杂度为O(1),因为它是一个原地排序算法,不需要额外的存储空间来排序。

直接插入排序的特性总结:

1. 元素集合越接近有序,直接插入排序算法的时间效率越高。
2. 时间复杂度:O(N^2)。
3. 空间复杂度:O(1),它是一种稳定的排序算法。
4. 稳定性:稳定。

总结

本篇博客就结束啦,谢谢大家的观看,如果公主少年们有好的建议可以留言喔,谢谢大家啦!

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

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

相关文章

JAVA小知识16:JAVA常用的API

一、Math 方法名说明public static int abs(int a)获取参数绝对值public static double ceil(double a)向上取整public static double floor(double a)向下取整public static int round(float a)四舍五入public static int max(int a,int b)获取两个int值中的较大值public s…

AI绘画Stable Diffusion 3 正式开源,AI生图格局迎来巨变!(附模型下载)

大家好&#xff0c;我是向阳 就在刚刚&#xff0c;Stable Diffusion 3 Medium 如约而至。 几天前&#xff0c;Stability AI 在社交平台 X 上官宣&#xff0c;SD3 Medium 将在 6 月 12 日正式开源。 这一次&#xff0c;没有跳票&#xff0c;它是真的来了。 20 亿参数大小&…

python数据分析-房价数据集聚类分析

一、研究背景和意义 随着房地产市场的快速发展&#xff0c;房价数据成为了人们关注的焦点。了解房价的分布特征、影响因素以及不同区域之间的差异对于购房者、房地产开发商、政府部门等都具有重要的意义。通过对房价数据的聚类分析&#xff0c;可以深入了解房价的内在结构和规…

C语言 | 文件操作(上)【必收藏】

文件操作&#xff08;上&#xff09; 1、使用文件的原因2、什么是文件3、二进制文件和文本文件4、 流的打开与关闭4.1 流和标准流4.1.1 流4.1.2 标准流 4.2 文件指针4.3 文件的打开和关闭 5、文件的顺序读写5.1 顺序读写函数介绍5.1.1 fputc与fgetc 1、使用文件的原因 首先来看…

据阿谱尔统计显示,2023年全球凹版印刷机市场销售额约为9.1亿美元

根据阿谱尔 (APO Research&#xff09;的统计及预测&#xff0c;2023年全球凹版印刷机市场销售额约为9.1亿美元&#xff0c;预计在2024-2030年预测期内将以超过2.54%的CAGR&#xff08;年复合增长率&#xff09;增长。 由于对软包装和印刷包装的需求不断增长&#xff0c;全球凹…

Qt绘图项目 - 简易表盘

发话少说&#xff0c;放码过来 widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget>QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent nullptr);~Widget();prot…

3-哈希表-81-四数之和-LeetCode18

3-哈希表-81-四数之和-LeetCode18 参考&#xff1a;代码随想录 LeetCode: 题目序号18 更多内容欢迎关注我&#xff08;持续更新中&#xff0c;欢迎Star✨&#xff09; Github&#xff1a;CodeZeng1998/Java-Developer-Work-Note 技术公众号&#xff1a;CodeZeng1998&#xff…

C#发送邮件

C#发送邮件代码&#xff0c;亲测可用。 using System; using System.Net; using System.Net.Mail;namespace MailSend {class Program{static void Main(string[] args){try{MailAddress receiver new MailAddress("666666666qq.com");//666666666qq.com 换成收件人…

告别混乱!7步打造精益管理模式,让企业效率翻倍

当下&#xff0c;企业要想立于不败之地&#xff0c;就必须注重管理效率的提升。然而&#xff0c;很多企业在追求高效运营的过程中&#xff0c;却常常陷入混乱和无效的努力中。那么&#xff0c;如何才能真正实现精益管理呢&#xff1f;深圳天行健精益管理咨询公司分享步骤如下&a…

【投稿优惠】2024年计算机科学与软件工程国际会议(ICCSSE 2024)

2024年计算机科学与软件工程国际会议 2024 International Conference on Computer Science and Software Engineering 会议简介 2024年计算机科学与软件工程国际会议是一个备受全球瞩目的学术盛会&#xff0c;旨在促进计算机科学和软件工程领域的学术交流与合作。此次会议将汇聚…

Android开发之音乐播放器添加排行需求

Music统计功能需求 1.记录歌曲名称与次数(歌曲播放结束算一次)&#xff0c;根据播放次数制作一个排行列表;&#xff08;开始说要记录歌手&#xff0c;后面debug发现这个字段没有&#xff0c;暂时不记录&#xff09; 2.记录播放歌曲的时长&#xff0c;时间累加&#xff1b;&…

基于安信可串口调试助手软件调试ESP8266串口WIFI模块ESP-01S应用功能

基于安信可串口调试助手软件调试ESP8266串口WIFI模块ESP-01S应用功能 ESP8266_01S引脚功能图ESP8266_01S原理图ESP8266_01S尺寸图检验工作1、USB-TTL串口工具(推荐使用搭载CP2102芯片的安信可USB-T1串口)与ESP8266_01S WiFi模块(推荐使用安信可ESP8266系列模组)接线(注意当…

使用谷歌 Gemini API 构建自己的 ChatGPT(一)

AI领域一直由OpenAI和微软等公司主导&#xff0c;而Gemini则崭露头角&#xff0c;以更大的规模和多样性脱颖而出。它被设计用于无缝处理文本、图像、音频和视频&#xff1b;这些基础模型重新定义了人工智能交互的边界。随着谷歌在人工智能领域强势回归&#xff0c;了解Gemini如…

HMI(人机交互)应用的15大领域,欢迎补充。

HMI&#xff08;Human-Machine Interface&#xff0c;人机界面&#xff09;可以应用于许多不同的场景和行业&#xff0c;包括但不限于以下几个方面&#xff1a; 工业控制系统&#xff1a;HMI在工业生产中广泛应用&#xff0c;用于监控和控制生产过程。例如&#xff0c;工厂中的…

数据库学霸笔记

Hi~&#xff01;这里是奋斗的小羊&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f4a5;&#x1f4a5;个人主页&#xff1a;奋斗的小羊 &#x1f4a5;&#x1f4a5;所属专栏&#xff1a;C语言 &#x1f680;本系列文章为个人学习…

前端问题整理

Vue vue mvvm&#xff08;Model-View-ViewModel&#xff09;架构模式原理 Model 是数据层&#xff0c;即 vue 实例中的数据View 是视图层&#xff0c; 即 domViewModel&#xff0c;即连接Model和Vue的中间层&#xff0c;Vue实例就是ViewModelViewModel 负责将 Model 的变化反映…

构建企业核心竞争力:拥有自主大模型,引领行业未来

前言 随着人工智能技术的飞速发展&#xff0c;大模型技术已经成为推动行业进步的重要力量。在这个变革的时代&#xff0c;作为一位具有前瞻性的企业家&#xff0c;您深知拥有自主大模型对于提升公司竞争力、引领行业未来的重要性。本文将为您详细介绍大模型的市场现状以及企业…

【机器学习】通用大模型VS垂直大模型,你更加青睐哪一方?

目录 前言AI大模型的战场&#xff1a;通用与垂直的分化通用大模型&#xff1a;广泛适用的利器垂直大模型&#xff1a;深入领域的利器谁能够形成绝对优势&#xff1f;结语通用大模型文章推荐 前言 AI大模型的战场正在分化&#xff1a; 通用大模型在落地场景更广泛毋庸置疑&…

暗色系B端界面有什么好处、应用场景、缺点、该如何设计。

B端管理系统界面蓝色系和浅色系的非常多&#xff0c;暗色系一般不作为首选&#xff0c;这背后是什么原因呢&#xff0c;如果真的要设计暗色系界面&#xff0c;该如何办呢&#xff0c;本文就解决这些问题。 一、暗色系B端界面相对于浅色系有什么好处 1. 减少眩光和视觉疲劳&am…

引入tinyMCE富文本框在vue3中的使用

实现效果&#xff1a; 官网地址&#xff1a;TinyMCE 7 Documentation | TinyMCE Documentation 1.下载依赖&#xff08;我使用的版本是5.0 目前最新版本到7了&#xff09; pnpm/npm install tinymce5.0.0 -S pnpm/npm install tinymce/tinymce-vue -S 2.在public文件夹下…