插入排序与希尔排序---C语言实现

其他排序

基数排序
归并排序
堆排序
快速排序
冒泡排序和选择排序

插入排序

核心思想:设置一个变量来记录当前插入位置的值,如果前一个位置大于该值则将前一个位置的值移入该位置,然后继续与前面元素的值比较,直至该值大于前一个值或者移动到最开头位置,插入该值。
实现方式
在这里插入图片描述例:对如上数组arr进行由小到大排序(注:一个元素肯定有序,插入元素之前一定是一个有序序列

注:这里橙色箭头仅表示此时要插入的位置,蓝色箭头表示与插入的值比较的位置

首先,我们应该定义一个变量来标记一下即将要插入的值:nValue = arr[1];
然后,数组从下标为1的位置开始与前面的元素进行比较,如果此时元素小于前一个元素,则此元素后移,然后继续与nValue继续与前一个值比较,直至小于或移动到最开头插入。过程如下:
在这里插入图片描述

由于arr[0] > nValue,因此将arr[0]向后移,即arr[1] = arr[0],然后nValuearr[0]的前一个值比较,由于arr[0]就是第一个元素,因此将nValue直接插入arr[0]位置处,然后新插入下一个元素(每插入一个元素,nValue都要标记新插入的元素)如下
在这里插入图片描述

如上,nValue = arr[i](此时i = 2,即nValue = 9),将nValue 与前一个值比较,由于nValue > 8,说明已经有序,此时直接进入下一次循环,如下
在这里插入图片描述

如上,nValue = 15,并且nValue > 9,已经有序,进入下一次循环,由于29页有序,这里直接跳到1处,如下

在这里插入图片描述如上,nValue = 1,由于nValue < 29,因此29需要后移,129的前一个元素15比较,如下
在这里插入图片描述

如上,nValue < 1515向后移动,115的前一个元素·9比较,如下,
在这里插入图片描述如上,nValue < 99向后移动,19的前一个元素·8比较,如下,
在这里插入图片描述如上,nValue < 88向后移动,18的前一个元素·3比较,如下,

在这里插入图片描述如上,nValue < 33向后移动,13的前一个元素比较,由于3已经是第一个元素,将nValue插入数组的第一个位置,并且开始插入新的数据,如下,
在这里插入图片描述如上,插入新的值nValue = 5,由于5 < 29,因此,移动29,nValue29的前一个值15比较,如下
在这里插入图片描述
如上,由于nValue < 15,因此,移动15,nValue15的前一个值9比较,如下
在这里插入图片描述
如上,由于nValue < 9,因此,移动9,nValue9的前一个值8比较,如下
在这里插入图片描述如上,由于nValue < 8,因此,移动8,nValue8的前一个值3比较,如下
在这里插入图片描述如上,由于nValue > 3,因此,插入nValue,进行下一次循环,如下
在这里插入图片描述
…依次按照上面的循环过程,最终可实现插入排序
代码如下

#include <stdio.h>

void InsertSort(int arr[],int nLen)
{
	if(arr == NULL || nLen <= 0) return;

	int nMark;
	int j;
	for(int i = 0;i < nLen-1;i++)
	{
			//标记无序的第一个元素
			nMark = arr[i+1];
			j = i + 1;
			//如果该元素小于前一个,前一个后移
				while(nMark <= arr[j] && j >= 0)
				{
					arr[j] = arr[j-1];
					j--;
				}
				arr[j+1] = nMark; 
	}

}
void Print(int arr[],int nLen)
{
	if(arr == NULL || nLen <= 0) return;

	for(int i = 0;i < nLen;i++)
	{
		printf("%d\t",arr[i]);
	}
	printf("\n");
}
int main()
{
	int arr[] = {8,5,3,91,123,12345,23,5672,12367,12,56,78,694,12345,567,10,11};

	InsertSort(arr,sizeof(arr)/sizeof(arr[0]));
	Print(arr,sizeof(arr)/sizeof(arr[0]));
	return 0;
}

最好时间复杂度:来的数组即为有序数组,O(n)

最坏时间复杂度:倒序数组,O(n²)

平均时间复杂度:O(n²)

空间复杂度:O(1)

稳定性:稳定

适用场合:数组较为有序时

希尔排序

核心思想:将数组的元素分组处理,每次折半len分组,比较i 与i+len这两个值得大小,若前者大于后者则交换值,之后继续折半,几次折半后数组变得相对有序,此时再利用插入排序。

通俗点讲就是先将数据分组,分完组之后再进行组内排序

排序过程:
1、分组
在这里插入图片描述初始时,数组长度nLen = 9;每次分组都在前一次的基础上折半,则第一次分组时nGap = nLen/2,(nGap 表示分组的间隔,当nGap > 0时退出循环)分组情况如下:
在这里插入图片描述
如上图所示,将原数组分成了四组,第一组元素:8、29、6;第二组元素:3、1;第三组元素:9、5;第四组元素:15、20;
每组之间相互排序,组内排序之后结果如下:
在这里插入图片描述

如上图所示,第一次分组排序后,基本上小数在前,大数在后,由于nGap = 4 > 0,因此继续折半分组,有nGap /= 2,此时nGap = 2,分组情况如下:
在这里插入图片描述

如上图所示,将原数组分成了二组,第一组元素:6、5、8、9、29;第二组元素:1、15、3、20;每组之间相互排序,组内排序之后结果如下:
在这里插入图片描述

由于nGap = 2 > 0,因此继续折半分组,有nGap /= 2,此时nGap = 1,分组情况如下:
在这里插入图片描述
如上图所示,当nGap = 1时,数组只分成了一组,此时排序结果如下:
在这里插入图片描述
如上图所示,当nGap = 1时,就已经排好序,此时再有nGap /= 2nGap = 0,退出循环。

可能我们会有疑问,直接利用插入排序就可以将数组排好序,那么为什么还要进行分组呢?
其实这就是根据插入排序的特点来的,由于插入排序对于小容量,相对有序的数组排序效率更高,但是对数组排序时数组的长度是不会变的,因此利用插入排序时我们总希望数组会相对有序,长度较小,但是实际排序时来的排序数组基本都是无序程度高,基于此,利用希尔排序分组的思想可以将数组容量减小后再组内排序,把数组变得相对有序,从而提高排序效率。

代码如下

#include <stdio.h>


void Print(int arr[],int nLen)
{
	if(arr == NULL || nLen <= 0) return;

	for(int i = 0;i < nLen;i++)
	{
		printf("%d\t",arr[i]);
	}
	printf("\n");
}
void InsertSort(int arr[],int nGroup,int nGap,int nLen)
{
	if(arr == NULL || nLen <= 0 || nGap <= 0 || nGroup < 0) return;

	int nValue ;//标记要插入的值
	int j;//向前遍历的变量
	for(int i = nGroup + nGap;i < nLen;i += nGap)
	{
		nValue = arr[i];

		//找出要插入的位置
		j = i-nGap;
		while(nValue < arr[j] && j >= 0)
		{
			//向后移动
			arr[j+nGap] = arr[j];
			j -= nGap;
		}
		//将值插入
		arr[j+nGap] = nValue;
	}
}
void ShellSort(int arr[],int nLen)
{
	if(arr == NULL || nLen <= 0) return;

	//分组
	int nGap = nLen/2;
	while(nGap > 0)
	{
		for(int i = 0;i < nGap;i++)
		{
			//插入排序
			InsertSort(arr,i,nGap,nLen);
			nGap /= 2;
		}
	}
}
int main()
{
	int arr[] = {45,675,2,89,13,68,4,67,4};
	ShellSort(arr,sizeof(arr)/sizeof(*arr));
	Print(arr,sizeof(arr)/sizeof(*arr));
	return 0;	
}

最好时间复杂度:来的数组即为有序数组,O(n)

最坏时间复杂度:倒序数组,O(n²)

平均时间复杂度:O( n1.3

空间复杂度:O(log2n)

稳定性:不稳定

适用场合:数组量大且较无序

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bug.Remove()

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值