带你真正了解插入排序和希尔排序

1.排序的概念以及应用

1.1 排序的概念及特点

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

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

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

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

1.2 排序的应用

 这些都体现出了排序的应用,在我们的视野中可以见到很多:

2.常见的排序算法

这是我们排序所涉及的全部排序方法,在这一节我们主要讲述插入排序的直接插入排序希尔排序

在计算机课程中涉及数据结构的过程中有意无意的会看见关于插入排序(如图1)和希尔排序(如图2)的实例图:

图一 直接插入排序

图二 希尔排序

我们在看见这些图的时候可能不太了解,书上说的也很模糊,以至于我们似懂非懂,或许只以为直接插入排序和冒泡排序都是一样的-->都是交换数值来进行排序, 其实他俩是是两个思维哟~

再简单介绍一下希尔排序:

官方解释:希尔排序是一种改进的直接插入排序算法,它先将待排序的序列划分为若干个子序列,对每个子序列进行直接插入排序,然后再整体上逐步缩小增量,使得整个序列更加有序,最后再进行一次直接插入排序。希尔排序的效率比直接插入排序算法要高。

从这一点我们可以知道希尔排序是建立在插入排序之上的

3.直接插入排序

插入排序是如何实现的?和冒泡排序思维差在哪里?

3.1有序的直接插入排序

有序插入排序思维表示图:

我们假设的插入的数在最后一个位置(即下标为end+1),在这里需要先创建一个容器tmp,用来存储a[end+1]的值,再让tmp与a[end]比较:

tmp<a[end]-->a[end+1]=a[end];end--;

tmp>a[end]-->a[end+1]=tmp;

(附上动图)更容易理解

这样我们就可以理解直接插入排序和冒泡排序的差距,冒泡排序只是单纯的交换数值从而进行排序

int end;
int tmp = a[end + 1];
//从后向前遍历
while (end >= 0)
{
	if (tmp < a[end])
	{
		a[end + 1] = a[end];
		end--;
	}
	else {		//有两种情况:1.遍历结束,end<0
		break;	//2.遇到tmp>a[end]
	}

}
//为什么会在while循环外面
//如果遇到end<0结束时,会直接结束,在else里面的时候会漏掉a[0]的位置
a[end + 1] = tmp;

3.2无序的直接插入排序

无序插入排序思维表示图:

理解有序的话无序就更容易理解:

前一个看成有序,然后进行插入排序;

前两个看成有序,然后进行插入排序;

前三个看成有序,然后进行插入排序;

.......................................................

我们就可以得到无序插入排序啦:

void InsertSort(int* a, int n)
{    //把前一个,前两个,前三个,前四个......看成有序
	for (int i = 0; i < n - 1; i++)
	{
		int end = i;
		int tmp = a[end + 1];
		//从后向前遍历
		while (end >= 0)
		{
			if (tmp < a[end])
			{
				a[end + 1] = a[end];
				end--;
			}
			else {		//有两种情况:1.遍历结束,end<0
				break;	//2.遇到tmp>a[end]
			}

		}
		//为什么会在while循环外面
		//如果遇到end<0结束时,会直接结束,在else里面的时候会漏掉a[0]的位置
		a[end + 1] = tmp;
	}
}

3.3无序插入排序==直接插入排序

嘿嘿~直接插入排序就已经讲完啦~不理解的可以去多看看动图结合代码就会更好的理解,理解之后,自己手搓一遍,就可以真正的消化啦~

光看不练假把式


4.希尔排序

在之前,我们就了解到希尔排序是建立在插入排序之上的,所以我们接下来详细了解一下希尔排序和直接插入排序的关系:

我们可以在直接插入排序的代码中进行改进:

        gap = 3;
		for (int i = 0; i < n - gap; i++)
		{
			int end = i;
			int tmp = a[end + gap];
			while (end >= 0)
			{
				if (tmp < a[end])
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			a[end + gap] = tmp;
		}

每次步进的下标大小为gap,

从第一个数(i==1)开始插入排序,步进大小为gap;

从第二个数(i==2)开始插入排序,步进大小为gap;

................

最后会得到一个近似有序的顺序,我们可以称之为预排序

当gap==1时,就等于直接插入排序-->可以将近似有序变为有序。

假如是很大的数我们就可以直接先进行预排序,然后再预排序,直到gap==1时,排序之后会得到我们想要的结果,最终代码实现如下:

void ShellSort(int* a, int n)
{
	int gap = n;
	while (gap > 1)
	{
		gap = gap / 3 + 1;
		for (int i = 0; i < n - gap; i++)
		{
			int end = i;
			int tmp = a[end + gap];
			while (end >= 0)
			{
				if (tmp < a[end])
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			a[end + gap] = tmp;
		}
	}
	
	
}

gap = gap/3+1;是为了确保最后一次为直接插入排序-->gap==1

也可以写:gap = gap / 2;也能达到这个效果

这样就完全说完了插入排序的直接插入排序和希尔排序,如果有不理解的可以直接私信我哟~

多看看,多想想,多画画图,就可以更好地理解啦~

加油加油!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Lye2077

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

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

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

打赏作者

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

抵扣说明:

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

余额充值