112.【C语言】数据结构之排序(详解插入排序)

目录

1.排序定义

2.插入排序

"插入"的含义

代码

函数框架

函数设计思路

以升序为例,分析插入的三种可能

单趟排序代码

优化后

将单趟排序代码嵌入到循环中

错误代码

两种改法

运行结果

时间复杂度


1.排序定义

使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作

2.插入排序

"插入"的含义

例如对于一个升序数列:3,4,5,11;现在要求插入7使得数列还能满足升序

显然改动后的数列应该是3,4,5,7,11

算法:设要插入的数字为k,从数列的最后一个元素开始,依次向前比较,直到满足

时,向原数列插入k

代码

小提示:写排序代码时,先写单趟排序,之后将其嵌入循环内,能较容易写出

函数框架

cpp 复制代码
void InsertSort(int* a, int n)
{

}

函数设计思路

设tmp为要插入的数字,end为有序数组的最后一个元素的下标,n为无序数组元素的个数,a为数组指针,将tmp插入到0,end的区间中,构成有序数列

以升序为例,分析插入的三种可能

①tmp大于所有已经排序的数

②tmp大小介于已经排序的数之间

③tmp小于所有已经排序的数

①tmp大于所有已经排序的数

例如有序数列int arr\[\]={1,3,5,8,10,21,37,40},此时arrend==40;现插入tmp==50,

第一次循环:tmp>arrend,直接arrend+1=tmp,循环结束

②tmp大小介于已经排序的数之间

例如有序数列int arr\[\]={1,3,5,8,10,21,37,40},此时arrend==40;现插入tmp==22

第一次循环:arrend==40,tmp<aend,于是arrend+1=arrend,end--,准备下一次循环

第二次循环:arrend==37,tmp<arrend,于是arrend+1=arrend,end--,准备下一次循环

第三次循环:arrend==21,tmp>arrend,满足条件,arrend+1=tmp,循环结束

③tmp小于所有已经排序的数

例如有序数列int arr\[\]={1,3,5,8,10,21,37,40},此时arrend==40;现插入tmp==0

省略前几次循环,第?循环,arrend==1,tmp<arrend,于是arrend+1=arrend,end--,准备下一次循环

如果进行了下次循环,end==-1,会造成越界访问,此时应该立即退出循环!执行arrend+1=tmp

显然退出循环有两种情况:1.end<0,即end==-1时 2.tmp>arrend

因此将所有可能的情况讨论全,循环退出的条件才能写全

单趟排序代码

cpp 复制代码
	int end;
	int tmp;
	while (end >= 0)
	{
		if (tmp > arr[end])
		{
			arr[end + 1] = arr[end];
			end--;

		}
		else
		{
			arr[end + 1] = tmp;
			break;
		}
	}
	if (end == -1)
	{
		arr[end + 1] = tmp;
	}

上面这样写略显麻烦,无论满足什么条件退出循环,最后都执行arrend + 1 = tmp;

优化后

cpp 复制代码
	while (end >= 0)
	{
		if (tmp > arr[end])
		{
			arr[end + 1] = arr[end];
			end--;
		}
		else
		{
			break;
		}
	}
	arr[end + 1] = tmp;

将单趟排序代码嵌入到循环中

main.c写入

cpp 复制代码
int main()
{
	int arr[] = { 1,6,2,4,7,3,9,0,-1,5 };
	printf("排序前:");
	PrintSort(arr, sizeof(arr) / sizeof(arr[0]));
	InsertSort(arr,sizeof(arr)/sizeof(arr[0]));
	printf("排序后:");
	PrintSort(arr, sizeof(arr) / sizeof(arr[0]));
	return 0;

错误代码

cpp 复制代码
void InsertSort(int* arr, int n)
{
	for (int i = 0; i < n; i++)
	{
		int end = i;
		int tmp = arr[i + 1];
		while (end >= 0)
		{
			if (tmp < arr[end])
			{
				arr[end + 1] = arr[end];
				end--;
			}
			else
			{
				break;
			}
		}
		arr[end + 1] = tmp;
	}
}

问题出在越界访问,当i==n-1时,执行int tmp = arri + 1;就越界了(arr的最后一个元素是arrn-1)!!

两种改法

改法1:改for的循环条件

cpp 复制代码
for (int i = 0; i < n-1; i++)

改法2:改tmp和end赋的初始值

cpp 复制代码
	for (int i = 0; i < n; i++)
	{
		int end = i-1;
		int tmp = arr[i];
    //省略......
    }

备注:循环的一开始,无序数组的首元素可构成一个有序数组,后面的数依次插入有序数组

运行结果

时间复杂度

最坏情况,每次内循环结束条件都为end==-1,时间复杂度

最好情况:提供的数组恰好是有序的,时间复杂度

结论:从时间复杂度来看,数组越接近有序,插入排序的时间复杂度越低,而希尔排序就是基于这个结论设计的,有关希尔排序的内容参见下篇文章

相关推荐
Discipline102910 分钟前
流水线运行是否正随时间变得更加集中?
算法
周玉奎先生12 分钟前
CGM系列水泥基灌浆料型号参数解析及安庆重点工程应用匹配
经验分享·笔记·零售
Logic10114 分钟前
C语言/数据结构位运算题解:异或XOR找出书柜中编号只出现一次的那一层——只出现一次的数字
c语言·数据结构·数组·位运算·时间复杂度·算法题·异或性质
海海不掉头发19 分钟前
软件项目管理学习笔记-从软件危机到敏捷与DevOps
笔记·学习·devops
lzhdim31 分钟前
C#不为人知的10个魔法特性:资深开发者也会震惊的底层奥秘
java·前端·javascript·算法·c#
weixin_3077791338 分钟前
C++代码实现MATLAB中的pade函数功能
开发语言·c++·算法·matlab
smj2302_7968265240 分钟前
解决leetcode第4064题至多一次取反能被k整除的最长子数组II
python·算法·leetcode
AIGC小尼44 分钟前
ACE-Step-1.5 本地 AI 音乐模型实战|text2music 歌词转人声,部署脚本、参数调优与排坑全记录
人工智能·算法·comfyui·ai漫剧
ProcessOn官方账号44 分钟前
java IO操作 你快速认识IO的相关基本知识
java·开发语言
YOLO数据集集合1 小时前
树木检测数据集 | 树木检测 树种分类 航拍林业 森林监测9131期
人工智能·算法·机器学习·分类·数据挖掘·林业·树种分类