希尔排序详解

前言:我们之前了解直接插入排序,知道如果待排序数组越有序 的话,则直接插入排序的时间效率越高 ,直接插入排序慢就慢在如果小的数在后面大的数在前面则每一个数要移动很多项,如果能够让大的数基本都在后面,小的数基本都在前面则大大提高直接插入排序的时间效率。那我们今天的希尔排序便是从这个视角优化直接插入排序而演变出来的。

希尔排序

算法思想 :先选定一个整数gap,将待排序的序列按照按照gap的距离分成若干分组 ,并对每一组数进行直接插入排序 ,然后gap=gap/3+1得到下⼀个整数gap再次分组排序 ,直到gap=1再进行最后一次直接插入排序,得到排序好的新序列


gap>1都是预排序目的是让序列更趋近于有序化(即小的在前大的在后),当gap==1时序列已经接近有序最后再整体直接插入排序使得有序化

希尔排序正是将我们先要排序得序列先分成一个个小的部分 ,但是增加排序序列个数 ,并最后形成较为有序得序列并运用直接插入排序形成新的有序序列 ,所以希尔排序也被称为缩小增量法

希尔排序动态图
代码实现
c 复制代码
void ShellSort(int arr[],int size)
{
	int gap = size;
	int temp = 0;
	int end = 0;
	while (gap > 1)
	{
		gap = gap / 3 + 1;
		for (int i = 0; i < size - gap; i++)
		{
			end = i;
			temp = arr[end +gap];
			while (end >= 0)
			{
				if (arr[end] > temp)
				{
					arr[end + gap] = arr[end];
					end = end - gap;
				}
				else
				{
					break;
				}
			}
			arr[end + gap] = temp;
		}
	}

}
希尔排序代码详解

第一部分:

c 复制代码
for (int i = 0; i < size - gap; i++)
		{
			end = i;
			temp = arr[end +gap];
			while (end >= 0)
			{
				if (arr[end] > temp)
				{
					arr[end + gap] = arr[end];
					end = end - gap;
				}
				else
				{
					break;
				}
			}
			arr[end + gap] = temp;
		}

gap将序列分成多个序列,在每一个分组中我们都用到直接插入排序,我们for (int i = 0; i < size - gap; i++)依次找到各数组有序序列最后一个元素下标,并将待插入元素插入其中。其最后一个有序序列的元素下标为n-gap-1所以循环结束条件为i < size - gap

关于最后循环条件i < size - gap的特别说明:

最后一个数组元素就是最后一个要插入的数据,所以最后一个数要插入的分割数组的有序序列下标即为最后一个有序序列的尾部即n-gap-1(下标),在图中即为元素6对应的下标。

第二部分:

c 复制代码
while (gap > 1)
	{
		gap = gap / 3 + 1;
		for (int i = 0; i < size - gap; i++)
		{
			......
		}
	}

这部分主要将是依次调整各分割出来的数组之间的距离,让距离逐渐减小gap = gap / 3 + 1的设置保证了gap最后能取到1从而进行最后的整个数组的直接插入排序,从而使数组有序。并在再次为1是不满足gap>1从而跳出循环。(gap也可以取gap = gap / 2 + 1

希尔排序的时间复杂度计算

外层循环:

c 复制代码
while (gap > 1)
	{
		gap = gap / 3 + 1;	
			......
	}

外层时间复杂度为O(log~2~n)gap = gap / 2 + 1)或者O(log~3~n)gap = gap / 3 + 1),即O(logn)

内层循环:

c 复制代码
for (int i = 0; i < size - gap; i++)
		{
			end = i;
			temp = arr[end +gap];
			while (end >= 0)
			{
				......
			}
			arr[end + gap] = temp;
		}

假设⼀共有n个数据,合计gap组,则每组为ngap{n\over gap}gapn个;在每组中,插⼊移动的次数最坏的情况下为gap*1 +2+3+...+(ngap{n\\over gap}gapn-1)
gap取值有(以除3为例):n3{n\over 3}3n n9{n\over 9}9n n27{n\over 27}27n ... 2 1

  • 当当gap为n3{n\over 3}3n时,移动总次数为n3{n\over 3}3n*(1+2)= n
  • 当gap为n9{n\over 9}9n时,移动总数为:n9{n\over 9}9n*(1 +2+3+...+8) = n9{n\over 9}9n*8(1+8)2{8(1+8)\over 2}28(1+8)= 4n
  • 最后一趟,gap=1即直接插⼊排序,内层循环排序消耗为n
    通过上面的分析,我们可以画出下面的曲线

因此,希尔排序在最初和最后的排序的次数都为n,即前⼀阶段排序次数是逐渐上升的状态 ,当到达某⼀顶点时,排序次数逐渐下降⾄n ,⽽该顶点的计算暂时⽆法给出具体的计算过程

对这个图像的分析:

一开始的增大时由于随着gap的减小for (int i = 0; i < size - gap; i++)这个循环次数变多,而由于刚开始数组还没有很大的有序化while (end >= 0)的循环次数改变还不明显,所以一开始逐渐增加,但随着数组逐渐有序化while (end >= 0)的循环开始减小,虽然for (int i = 0; i < size - gap; i++)还是增加,但总体呈现下降趋势

关于最后希尔排序时间复杂度说明

希尔排序时间复杂度不好计算,因为gap 的取值很多,导致很难去计算,因此很多书中给的希尔排序的时间复杂度都不固定,但在严蔚敏老师的《数据结构(C语⾔版)》中给出的时间复杂度为O(n1.3

相关推荐
小雨下雨的雨4 小时前
井字棋AI机器人实现详解 - Minimax算法实战-鸿蒙PC Electron框架完成
前端·人工智能·算法·华为·electron·鸿蒙
xieliyu.6 小时前
Java算法精讲:双指针(三)
java·开发语言·算法
一条小锦吕*7 小时前
基于Spring Boot + 数据可视化 + 协同过滤算法的推荐系统设计与实现(源码+论文+部署全讲解)
spring boot·算法·信息可视化
如竟没有火炬8 小时前
最大矩阵——单调栈
数据结构·python·线性代数·算法·leetcode·矩阵
8Qi89 小时前
LeetCode 1143 & 718:最长公共子序列 / 最长重复子数组
算法·leetcode·职场和发展·动态规划
绿算技术9 小时前
万卡推理集群存储选型分析:从核心架构到应用视角
大数据·科技·算法·架构
想吃火锅100510 小时前
【leetcode】1.两数之和js版
javascript·算法·leetcode
net3m3311 小时前
一阶软件低通滤波器算法
人工智能·算法
水木流年追梦11 小时前
大模型入门-大模型优化方法12-YaRN 长文本外推技术
人工智能·分布式·算法·正则表达式·prompt