目录
前言
本文将介绍八大排序之一,Shell排序,Shell排序实际为直接插入排序的优化,使其时间复杂度降低。
“shell排序,又名希尔排序,被称作缩小增量排序,是1959年首次提出的。主要思想:假设待排序原素序列涵盖n个原素,取一个整数increment(小于n)作间隔,把所有的元素分为increment个子序列,全部距离是increment的元素,放在同一个子序列中,在每一个子序列中分别实施直接插入排序”,Shell排序的执行时间依赖于增量序列。
上次了解的插入排序,我们会发现一个特点,那就是如果数据序列越有序,我们调用直接插入排序时数据移动的次数就会减少。
由此科学家希尔提出这种算法,使排序的时间复杂度降于O( n ^ 2 ) 之下。
方法就是将原来的数据组分为多组,先进行局部的直接插入排序,达到数据的基本有序,再最后进行整体的排序,使数据真正有序。
希尔排序: 直接插入排序的优化
稳定性: 不稳定
时间复杂度O(n^1.3~1.5)(和增量数组取值有关)
空间复杂度O(1)
一、分组规则
我们先给出一组数:18 21 8 9 10 7 6 5 91 3 66 77 35 22 29
如果我们按照以上方式简单的进行分组,各组进行简单的排序过后,我们发现好像并没有什么明显的变化,距离我们想要的有序还差的较远,所以这是一种错误的分组。
因此,我们需要采取跳跃分割的策略:将相距某个“增量”的数据组成一个子序列,这样才能保证整体数据的相对有序,而不是局部有序。
注意,这里最后的增量大小必须为1,否则数据未达到完全排序。
在这里我们分别依照5,3,1的增量间隔进行分割:
到这里是不是发现在整体上出现了前面数据值较小,后面数据值较大的变化。
当我们进行最后一次整体排序后,达到以下的结果
并且因为分组后数据跳跃移动的缘故,对数据的移动次数大大减少,降低了时间复杂度。
二、代码实现
此处代码实现与直接插入排序大致相同
void Shell(int* ar, int n, int gap)
{
///此处与直接插入排序大致相同,仅仅增量部分存在差异
for (int i = gap; i < n; i++)
{
int num = ar[i];
int j = i - gap;
for (j; j >= 0; j -= gap)
{
if (ar[j] > num)
{
ar[j + gap] = ar[j];
}
else
{
break;
}
}
ar[j + gap] = num;
}
}
void Shell_Sort(int* ar, int n)
{
///提供增量数组
int gap[] = { 5,3,1 };
int length = sizeof(gap) / sizeof(gap[0]);
for (int i = 0; i < length; i++)
{
Shell(ar, n, gap[i]);
}
}
总结
以上就是今天要学习的内容,本文介绍了Shell排序的排序过程和代码实现,Shell排序的时间复杂度在O( n ^ 2 ) 之下,是较为快速的几种算法之一,其在增量的思想需要进行认真思考和学习。