直接插入排序的代码实现及其时间复杂度

97 阅读3分钟

直接插入排序的代码实现及其时间复杂度的计算

直接插入排序的基本思想

斗地主是一个几乎我们每个人都玩过的纸牌游戏,当我们摸牌的时候,要边摸牌边理牌,也就是按顺序整理好自己摸上来的牌,就比如下面这一手牌,如果是你,会如何来理牌呢?

image.png 如果按照升序来排的话,只需将4和5移到6的左侧,再将3移到最左侧,这个牌就算是理好了,这里我们理牌的方法,就称作:直接插入排序法。

直接插入排序法的基本思想是:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列,实际中我们玩扑克牌时,就用了插入排序的思想。 接下来我们用一个无序的数组来举例(假设要排成升序),先上代码:

代码思路:

首先,将数组内的第一个元素arr[end]看作一个已经排好序的数组(只有一个数),让其后面一个元素tmparr[end]以及前面的数作比较,如果tmp较小,则将arr[end]的位置向后移动一位;如果arr[end]较大,则说明已经排好序,位置不动(如下图):

image.png 如图所示:将a看作一个已经排好序的数组如果a>b,则a移到b的位置,而b继续往前比较,此时b向前移动以后,前面已经没有数据,因此b此时就存放到a的地方;如果a<b,则此时a和b就已经排好序了,以此类推。

不过要注意的是,插入排序不是在两个数比较之后做值交换,而是把大的(假设要升序排列)往后移动一位,小的留着和前面的数据接着比,比如下面的数组(如下图):

image.png

如图所示:a前面的数已经排好了序,假设a此时的值为1,那么a就要首先跟5进行比较,1比5小,所以5要移动到a的位置,然后a再向前移动一位,此时arr[end]就变成了4,tmp还是等于a(如下图):

image.png

接着a又和4比较,以此类推,当a前面没有数比它大时,则此时a就已经排好序了,tmp就会变成下一个要插入的数b,而arr[end]会变成b的前面一个数,也就是5(如下图),以此类推,最终完成排序:

image.png

*详细步骤与介绍在下方代码实现和注释当中⬇

直接插入排序的代码实现:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>

void PrintArry(int* arr, int n)                /*打印函数*/
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}

void InsertSort(int* arr, int n)              /*直接插入排序函数(升序)*/
//将数组的第一个元素当成抽到的第一张牌
//后面抽到一张牌就与之相比,如果比它大,就插在其右边;如果比它小,就插在其左边
//将每次抽到的牌都与最右边的牌相比,如果小就往前找,如果大就往后放,跟斗地主摸牌一个道理
{
	for (int i = 0; i < n-1; i++)
	//要注意是i<n-1;因为后面有end=i和end+1,不能越界
	{
		int end = i;                  /*end为有序数组的、最后一个元素的下标*/
		int tmp = arr[end + 1];       /*tmp里面存放的是要插入的数*/
		while (end >= 0)              /*排序步骤*/
		{
			if (tmp < arr[end])
			{
				arr[end + 1] = arr[end];   /*arr[end]的位置向后移一位*/
				end--;                     /*看前面的数还有没有比tmp大的*/
			}
			else 
			{
				break;      
                                //排好序后break的三种情况: 
                                //1、tmp 1 2 3...      
                                //2、1 2 tmp 4...
                                //3、1 2 3 tmp...
			}
		}
		arr[end + 1] = tmp;
                //tmp已经排完序了
	}
	PrintArry(arr, n);  /*打印函数*/
        
}


int main()
{
	int arr[] = { 0,3,1,5,2,4,6,9,7,8 };
	int len = sizeof(arr) / sizeof(arr[0]);
	InsertSort(arr, len);

	return 0;
}

输出结果:

image.png

时间复杂度分析:

假设排序为升序:

插入排序的时间复杂度是多少? 首先我们要明白,当数组为逆序时,排序次数最多,为1+2+3+...+n-1次: 当一个公差为1的降序数组有n个元素时,如果要将该数组升序排列,

image.png

从第二个元素n-1开始,排序次数为1次,因为比n-1大的数就1个,那么比n-1大的数都要跟n-1作比较之后,再把位置向后移动1位,此时的数组为:

image.png

再从第三个元素n-2开始,以此类推,直到最后一个元素1,排序总次数就为: image.png

也就是一个公差为1的等差数组,因此最后计算结果就是:

image.png

因为该式子次数最高的项为n^2,去掉常数和次数低于2的项后,时间复杂度为:

image.png

因此,当数组为逆序时,时间复杂度最大,那么当时间复杂度最小的时候当然就是数组为顺序的情况了:

image.png

还是从第二个元素2开始,拿2与1做比较,因为2比1大,所以进行排序操作时,2的位置不动,但这还是相当于进行了一次排序操作,因此排序次数为1;

image.png

接下来到第三个元素3,拿3跟2进行比较,3比2大,因此3的位置也不动,但这也相当于进行了一次排序操作,因此排序总数为1+1=2;

以此类推,因为该数组总共有n个元素,因此要进行n-1次排序操作,去掉常数1,则该情况下的时间复杂度就为:

image.png

时间复杂度总结:

直接插入排序的时间复杂度为:

image.png

逆序时情况最坏,时间复杂度为:

image.png 顺序时情况最好,时间复杂度为:

image.png

以上就是本篇文章的全部内容,如果你觉得对你多少有些帮助,可以点个赞或者收藏一波支持一下哦,欢迎各位大佬批评指正,咱们下次再见!