希尔排序是 D . L . S h e l l D.L.Shell D.L.Shell于1959年提出来的一种排序算法,在这之前排序算法的时间复杂度基本都是 O ( n 2 ) O(n^2) O(n2)的,希尔排序算法是突破这个时间复杂度的第一批算法之一。
希尔排序:是对直接插入排序进行的一种改进排序算法,采用跳跃分割的方法,将整个待排序列分割成若干个子序列,对这些子序列进行直接插入排序,使其得到的结果基本有序,然后再不断缩减分割的区间,使其完全有序
基本有序:就是小的关键字基本在前面,大的基本在后面,不大不小的基本在中间,像{2,1,3,6,4,7,5,8,9}这样的序列就可称其为基本有序
跳跃分割策略:将相距某个“增量”的纪录组成一个子序列,这样才能保证在子序列内分别进行直接插入排序后得到的结果是基本有序而不是局部有序
根据这一思想得出下面的代码:
void ShellSort(vector<int>& vec)
{
int i,j;
int tmp;
int increment = vec.size();
do
{
increment = increment/3 + 1; //初始跳跃间隔
for (i = increment + 1; i <= vec.size(); ++i)
{
if (vec[i - 1] < vec[i - increment - 1])
{
//对每一个小序列执行直接插入排序
tmp = vec[i - 1];
for (j = i - increment - 1; j >= 0 && vec[j] > tmp; j -= increment)
vec[j + increment] = vec[j];
vec[j + increment] = tmp;
}
}
}while (increment > 1); //当跳跃间隔大于1继续
}
简单的测试
测试序列:9,1,5,8,3,7,4,6,2
下面从代码及结果进行一个简单的分析:
- 首先从代码来看,这基本就是一个直接插入排序的复刻版,但与之有所不同的在于设置了跳跃间隔,初始我们设置为总长度的三分之一加1,这样能快速得到一个基本有序的序列,从结果我们也可以看的出来,即第5次排序也是第一轮排序结束的时候序列形成了一个基本有序的序列;
- 然后还有一点在于复刻直接插入排序时注意我们是对每一个子序列进行直接插入排序,所有这个间隔千万不能忘记( j − = i n c r e m e n t j -= increment j−=increment);
- 还有最后一点就是我们应该什么时候跳出循环,即排序结束,那通过代码我们知道是当 i n c r e m e n t < = 1 increment <= 1 increment<=1时跳出,其实这个很容易理解,因为可以计算得出在跳出循环的前一个状态必然 i n c r e m e n t = 1 increment = 1 increment=1(这也是增量设置的一个约束),即已经间隔为1判断过了,所以就跳出循环
时间复杂度分析
通过对上述代码及结果的简单分析,相信大家都有所收获,希尔排序的关键并不是随便分组后各自排序,而是将相隔某个“增量”的记录组成一个子序列,实现跳跃式的移动,使得排序效率提高
关于增量选取的问题,到目前为止还是一个数学难题,迄今为止还没有任何人找到一种最好的增量序列。不过大量的研究表明,当增量序列为 d l t a [ k ] dlta[k] dlta[k] = 2 t − k + 1 2^{t-k+1} 2t−k+1 - 1 (0 ≤ \leq ≤ k k k ≤ \leq ≤ t t t << log 2 ( n + 1 ) \log_2{(n+1)} log2(n+1))时,可以获得不错的效率,其时间复杂度为O( n 3 / 2 n^{3/2} n3/2),好于直接排序的O( n 2 n^{2} n2), 需要注意的是,增量序列的最后一个增量值必须为才行
- 由于记录是一种跳跃式的移动,希尔排序并不是一种稳定的算法
测试代码:
#include <iostream>
#include <vector>
using namespace std;
void ShellSort(vector<int>& vec);
void PrintStep(vector<int> vec, int n, int i);
void PrintResult(vector<int> vec, int n);
void ShellSort(vector<int>& vec)
{
cout << "--------------希尔排序--------------" << endl;
int i,j;
int k = 1;
int tmp;
int increment = vec.size();
do
{
increment = increment/3 + 1;
for (i = increment + 1; i <= vec.size(); ++i)
{
if (vec[i - 1] < vec[i - increment - 1])
{
tmp = vec[i - 1];
for (j = i - increment - 1; j >= 0 && vec[j] > tmp; j -= increment)
vec[j + increment] = vec[j];
vec[j + increment] = tmp;
}
PrintStep(vec, vec.size(), k);
++k;
}
}while (increment > 1);
cout << "最终排序结果为:";
PrintResult(vec, vec.size());
}
void PrintStep(vector<int> vec, int n, int i)
{
cout << "第" << i << "次排序结果: ";
for (int j = 0; j < n; ++j)
cout << vec[j] << ' ';
cout << endl;
}
void PrintResult(vector<int> vec, int n)
{
for (int j = 0; j < n; ++j)
cout << vec[j] << ' ';
cout << endl;
}
int main(int argc, char **argv)
{
int a[] = {9,1,5,8,3,7,4,6,2};
vector<int> vec(a, a+9);
ShellSort(vec);
return 0;
}