数据结构与算法:希尔排序
一、概念及其介绍
希尔排序是插入排序的一种,它是针对直接插入排序算法的改进。
希尔排序又称缩小增量排序,它通过比较相距一定间隔的元素来进行,各趟比较所用的距离随着算法的进行而减小,直到只比较相邻元素的最后一趟排序为止。
希尔排序的基本思想是将待排序的元素分成多个子序列,每个子序列通过插入排序进行排序。初始时,整个序列被分成多个子序列,每个子序列包含相隔一定增量的元素。随着增量的逐渐减小,子序列的元素逐渐合并,最终当增量减至1时,整个序列完成排序。
希尔排序的时间复杂度是,空间复杂度为常数阶
。
首先,把n个数分成gap组进行预排序
int gap = 3;
for (size_t i = 0; i < n - gap; i += gap)
{
int end = i;
int tmp = a[end + gap];
while (end >= 0)
{
if (tmp < a[end])
{
a[end + gap] = a[end];
end -= gap;
}
else
break;
}
a[end + gap] = tmp;
}
end的位置要小于n - gap,如果大于,会越界。这种方法是一组一组的走完,先走完红色的,让它成为有序,然后再走绿色的,最后走蓝色的。但是这样总体上还不是有序的,所以,最后还要走一次插入排序。
那有没有直接完成排序的方法呢?还有就是gap为什么是3,可以是其他数字吗?
我们首先要知道
gap越大,可以越快跳到后面,小的数就可以越快跳到前面,但越不接近有序。
gap越小,跳的越慢,但是越接近有序。当gap == 1时,相当于插入排序,其结果就有序了。
所以我们接下来代码要改进了
void ShellSort(int* a, int n)
{
int gap = n;
while (gap > 1)
{
gap = gap / 3 + 1;
for (int j = 0; j < gap; j++)
{
for (size_t i = j; i < n - gap; i += gap)
{
int end = i;
int tmp = a[end + gap];
while (end >= 0)
{
if (tmp < a[end])
{
a[end + gap] = a[end];
end -= gap;
}
else
break;
}
a[end + gap] = tmp;
}
}
}
}
gap = gap / 3 + 1,不管gap为什么值,都可以保证gap最后都为1,当gap==1时相当于一次直接插入排序,结果为有序。当然也可以不除3,可以除其他值,但是要保证gap最后为1.
外层for循环完成gap组依次排序,内层for循环完成每组的排序。一组完成排序再接着下一组进行排序。
还有一种并着走的写法,但是从效率上来说没有改变。
void ShellSort(int* a, int n)
{
int gap = n;
while (gap > 1)
{
gap = gap / 3 + 1;
for (size_t i = 0; i < n - gap; ++i)
{
int end = i;
int tmp = a[end + gap];
while (end >= 0)
{
if (tmp < a[end])
{
a[end + gap] = a[end];
end -= gap;
}
else
break;
}
a[end + gap] = tmp;
}
}
}