经典算法(四)----希尔排序----图解法让你快速入门

引言

     只要设计到数据,就会涉及到数据的排序问题,比如给你随机给你五个整数  3,1,5,2,4 。让你从小到大进行排序,那我们该怎样才是实现对这些整数的排序呢 ?

    答案是多种多样的,比如用插入排序、希尔排序、堆排序、归并排序、快速排序等等,这些排序方法都可以实现对整数排序,而这篇文章要讲的就是希尔排序

本文将从以下几个问题对希尔排序进行分析和讲解:

  1. 什么是希尔排序?
  2. 希尔排序的大概过程是什么?
  3. 怎样用代码实现希尔排序?
  4. 希尔排序的代码详解。
     

什么是希尔排序?

下面看百度百科对希尔排序的定义:

希尔排序(Shell's Sort)是插入排序的一种又称“缩小增量排序”(Diminishing Increment Sort),是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。该方法因D.L.Shell于1959年提出而得名。

希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。 

我的理解:还记得插入排序的方法吗,学习希尔排序一定要类比着插入排序。

  1. 插入排序就是首先选定第一个数为有序序列,然后往后的每个元素都往有序序列里面插入,直到排序结束。
  2. 而希尔排序则是插入排序的改进版,希尔排序进行好多轮插入排序(虽然进行了多轮插入排序,但是时间复杂度竟然比进行一轮的插入排序还短。你说厉不厉害!!!)
  3. 当然了,虽然是多轮插入排序的操作,但是肯定不是单纯的重复插入排序的操作。希尔排序里面的每次插入执行插入操作和单纯的插入操作有区别,在单纯的插入操作中,一个挨一个的进行插入操作,而希尔排序中的每轮插入操作就不是一个挨一个的比较插入,只是隔几个元素比较插入一次。
  4. 注意,希尔排序虽然可以理解成多次插入排序,但是这里的插入排序间隔不一样。这里的隔几个元素插入一个就被称为增量,而且这个增量还不是固定的,即多次插入排序增量每次都是不一样的

下面先看一个动图:

再看这个动图的起始图片

       他一共有10个元素,一共有5中颜色(五组),每种颜色有两个(每两个元素间隔为5),前面的间隔就是上面所说的增量。

截止到现在位置,你只需要知道希尔排序可以类比成多次插入排序,这个希尔排序中有增量这个名词,而且你还有会简单的插入排序就可以了!!!

希尔排序的大概过程是什么?

下面说几个问题:

  1. 关于上面说的增量,希尔排序中的每轮插入排序增量都不同,而且没有规定增量为多少最合适,但是在进行最后一轮进行插入排序的时候,它的增量必须是1,就像简单的插入排序那样。
  2. 假设增量我们用变量 x存储,一般增量的选取采取x=x/3+1这种方式。就如第一轮变量为5,那下一轮变量就是2(5/3+1)。下一轮为1(2/3+1)。知道把增量为1的一轮进行完成就可以了,如果某一轮插入排序结束了,你想问是否结束了,你只需要看看看增量是否为1就可以了。

下面我们拿3,1,5,2,4这组数来说明希尔排序的具体过程

  1. 一共五个数,那么第一轮增量就为5/3+1=2。
  2. 上面一共是三组数据,每组数据都要进行 各自的插入排序,每组数据各自进行插入排序后,都要变成有序的,至于怎样进行插入排序这里就不多说了。
  3. 虽然是进行了三组插入排序,但是这三组插入排序是同时进行的,就是像插入排序那样,一个挨着一个往后走,但是每个数只和自己那一组的数进行比较。具体可以参考上面的动图。
  4. 它的具体过程:第一的第一个数3默认为有序序列,第二组的第一个数1默认为有序数列,第三组的第一个数4默认为有序序列(这个第三组可以不考虑,因为他就一个数)。我们看上图的3,1,5,2,4。(加粗是已经有序)
  5. 从第三个数5开始看起(前两数都在自己各自组里有序),因为5是和3一组的,那么拿5和3进行比较,发现3<5,不做处理
  6. 继续看第四个数2,2和1是一组的,发现1<2,不做处理。
  7. 截止到现在,增量为2的各组已经处理结束了,判断发现增量不为1(增量是排序的结束条件)。然后执行2/3+1=1.
  8. 再对这一组进行增插入排序即可(和正常的插入排序一样)。排序完成就是1,2,3,4,5

总结:上面的过程希尔排序的全过程,这时候就该有人问了,上面的希尔排序分两轮,第一轮什么也没做,第二轮就和正常的插入排序一样,拿第一轮不是没用了吗? 按上面的例子来说,确实是这样,但是希尔排序的过程就是我上面弄写的过程,就是要进行一轮一轮的插入排序,直到最后一轮插入排序结束(增量为1,和正常的插入排序一样)。换种说法就是在希尔排序的最后一轮插入排序之前,所有轮的插入排序都是为了做铺垫,为最后一轮的插入排序做准备。

上面的做准备的意思就是让整个数组相对有序(相对来说小的元素在前面,大的元素在后面)。经过了多轮的插入操作之后,再进行最后一轮插入操作就会有很的不同(上面对5个数排序体现不出来,如果对更多的数排序就能体现出来)。

其实希尔排序算是一个 “划时代” 的排序算法,在希尔排序之前有选择排序,冒泡排序,插入排序,但是平均时间复杂度都是O(n^2),而希尔排序时间复杂度就低于了O(n^2)。自打希尔排序出现之后,才又出来堆排序,快排这些。

 怎样用代码实现希尔排序?

代码:

#include<iostream>
#include<time.h> 
using namespace std;
//希尔排序函数    不稳定 
void ShellSort(int arr[],int len)
{
	int temp;
	//increment是增量 
	int increment=len;
	do{
		increment=increment/3+1;
		for(int i=increment;i<len;i++)
		{
			if(arr[i-increment]>arr[increment])//这是一个优化,如果看不懂,可以默认他没有 
			{
				temp=arr[i];
				int index=i-increment;//类似插入排序 
				while(index>=0&&arr[index]>temp)
				{
					arr[index+increment]=arr[index];
					index-=increment;
				} 
				arr[index+increment]=temp;
			}
		}
	} while(increment>1);
}
//输出数组的值
void printf(int arr[],int len)
{
	for(int i=0;i<len;i++)
		cout<<arr[i]<<" ";
	cout<<endl;
}
int main()
{

	//要排序的数组 
	int arr[]={3, 44,38, 5,47,15,36,26,27,2 ,46,4 ,19,50,48};
	int len=15;//要排序的数组长度 
	//排序 
	ShellSort(arr,len);
	//输出 
	printf(arr,len);
	   
	return 0;
}

运行结果:

希尔排序代码详解

  1. 上面代码写了两个函数,一个属printf函数是这个数输出排序后的数组
  2. 另一个函数就是ShellSort函数,函数一开始就定义了一个增量,下面采取的是do-while循环(为了保证最后一次增量为1)。在这循环里面首先就是增量的计算公式,每一轮都要重新计算一个新的增量,接着就是一个for循环,从头到尾把数遍历一遍,但是这个一般的插入排序是有区别的。虽然是一个挨一个,但是只和自己本组的数进行比较。

本文参考以及引用:

百度百科

图片动画
 

猜你喜欢

转载自blog.csdn.net/weixin_44820625/article/details/106751283