希尔排序详解

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

希尔排序

算法思想 :先选定一个整数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

相关推荐
kkkkk0211067 小时前
【Rust创作】Rust 错误处理:从 panic 到优雅控制
开发语言·算法·rust
爱学习的小鱼gogo7 小时前
pyhton 螺旋矩阵(指针-矩阵-中等)含源码(二十六)
python·算法·矩阵·指针·经验·二维数组·逆序
坚持编程的菜鸟7 小时前
LeetCode每日一题——二进制求和
c语言·算法·leetcode
Glink8 小时前
现在开始将Github作为数据库
前端·算法·github
WWZZ20258 小时前
快速上手大模型:机器学习6(过拟合、正则化)
人工智能·算法·机器学习·计算机视觉·机器人·slam·具身感知
ceclar1238 小时前
C++Lambda表达式
开发语言·c++·算法
2401_841495649 小时前
【强化学习】动态规划算法
人工智能·python·算法·动态规划·强化学习·策略迭代·价值迭代
WWZZ20259 小时前
快速上手大模型:机器学习5(逻辑回归及其代价函数)
人工智能·算法·机器学习·计算机视觉·机器人·slam·具身感知
DuHz9 小时前
基于频率分集阵列的MIMO雷达联合距离角度估计——论文阅读
论文阅读·算法·汽车·信息与通信·毫米波雷达