希尔排序(循序渐进版)

前言

每个排序方法的速度都有各异,其中,希尔排序算是顶流,速度相当的快,但也相对难理解点,这篇文章将向你讲解希尔排序

一、希尔排序是什么?

希尔排序是基于直接插入排序延伸出来的一种排序,因为他的速度快而出名,总体思路就是将数组进行分组并进行预排序,然后再插入排序。

二、希尔排序的初阶

在学习希尔排序之前,需要先掌握直接插入排序的思想,否则理解起来将会相当的难
点击此处跳转到直接插入排序

我们需要再直接插入排序的基础上,对数组进行分组(规定每组相隔gap个元素)

gap越小,排序所需要的时间就越长,但是越接近有序
gap越大,排序所需要的时间就越短,但是越不接近有序

在这里插入图片描述
如图所见,我们已经将这个数组分成了红绿蓝三组

然后,我们对这三组进行单独的直接插入排序,排序完之后再依次放回原位
在这里插入图片描述

预排序的好处:

  1. 可以将数组变得相对有序
  2. 可以让那些大的数更快到最后,让那些小的数更快到前面

正常来说,9需要走动9次才能到达最后,但是现在只需要3次
0需要9次才能到达最前面,但是现在只需要3次

之后的话只需要进行插入排序就可以将数组排好序了(速度上比直接插入排序快上不少)


void ShellSort(int* arr, int size)
{
	//确定好分多少组
	int gap = 3;
	
	//每一组都要进行排序
	for(int j = 0; j < gap; j++)
	{
		//排一组(红、蓝、绿)
		//因为等一下需要像直接插入排序那样访问最后一个数
		//而最后一个数是end + gap
		//为了防止越界访问,所以end的范围不能超过size - gap
		for (int i = j; i < size - gap; i+= gap)
		{
			//i += gap是为了每次都是访问到自己颜色的那些元素
			
			int end = i;
			int tmp = arr[end + gap];//(要被排序的最后一个元素)
			//控制排序的起点
			while (end >= 0)
			{
				//升序
				if (tmp < arr[end])
				{	
					arr[end + gap] = arr[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
	
			Swap(&arr[end + gap], &tmp);
		}
	}
	//排序好之后就直接排序
	InsertSort(arr, size);
}

另外,以上的代码也是可以进行优化的,就像是三个循环太多了,能不能让循环少一点?

第一种:
外面的两层循环:for(j = 0; j < gap; j++)
↓			  {for(i = j; i < size - gap; i+=gap)}
↓			  (红组先排,然后轮到蓝组,最后轮到绿组)
转化成一层
第二种:
			   for (i = 0; i < size - gap; i++)
			   这里运用的就是多组并排的方式
			  (因为直到最后,end每个位置都会出现一遍)
			  (倒不如直接排红组一轮到蓝组一轮到绿组一轮
			  再到红组二轮再到蓝组二轮......)

在这里,两种都是可以的,第一种的话更好理解,第二种的话比较巧妙
可以根据需求进行选择

三、希尔排序的进阶

虽说gap = 3看起来挺不错的,但是万一有一万个数据呢?

如果是一万个数据,分成三组,那每一组都有3333个元素

所以,gap必须和size(元素个数)有关

因此可以得出一种新的思路:

进行多次的预排序,多次改变gap,然后再直接插入排序

另外,当gap等于1的时候,就代表着直接插入排序了(因为gap代表分组的数量)

所以我们可以在改变gap的时候,让gap最后一定是为1的,这样的话也能省略掉直接插入排序函数引用的过程了

改变gap的方式:

int gap = size;
	
	while (gap > 1)
	{
		//此处的/3 + 1就是为了让gap最后一个一定为1
		//举例:如果没有+1,那么当数组的元素个数为6时
		//  6/3 = 2;   2/3 = 0 (那就死循环了)
		gap = gap / 3 + 1;
		......
	}

将以上代码进行合成就可以得到以下的完整代码了

四、完整代码

void ShellSort(int* arr, int size)
{
	int gap = size;
	while (gap > 1)
	{
		gap = gap / 3 + 1;
		

		for (int i = 0; i < size - gap; i++)
		{

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

			Swap(&arr[end + gap], &tmp);
		}

	}
	
}

总结

以上就是今天要讲的内容,本篇文章着重讲解了希尔排序的原理和思路,在学习的路上还会有很多知识的分享,今后将会持续更新,敬请关注!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

CtrlZ大牛

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

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

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

打赏作者

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

抵扣说明:

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

余额充值