希尔排序又称缩小增量排序法。首先,将待排序的关键字序列分成若干个较小的子序列,对子序列进行直接插入排序,使整个待排序序列排好序。
希尔排序是基于插入排序的以下两点性质而提出改进方法的:
1、插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率。
2、插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位。
希尔排序的过程:
在希尔排序中,各子序列的排序过程是相对独立的,具体实现时,并不是对一个子序列进行完全排序,再对另一个子序列进行排序。
希尔排序从第一个子序列的第二个元素开始,顺序扫描待排序记录序列,对首先出现的各子序列的第二个元素,分别在各子序列中进行插入处理;然后对随后出现的各自序列的第三个元素,分别在各子序列中进行插入处理,直到处理完各子序列的最后一个元素。
增量的取法:一般采用d = [d/3] + 1
逆转数:在它之前比此关键字大的关键字的个数
逆转数之和就是排序过程中插入某一个待排序记录所需要移动记录的次数。
逆转数为0则表明整个排序完成。
稳定性:
由于多次插入排序,我们知道一次插入排序是稳定的,不会改变相同元素的相对顺序,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱,所以shell排序是不稳定的。
本质上讲,希尔排序算法是直接插入排序算法的一种改进,减少了其复制的次数,速度要快很多。 原因是,当n值很大时数据项每一趟排序需要的个数很少,但数据项的距离很长。当n值减小时每一趟需要和动的数据增多,此时已经接近于它们排序后的最终位置。 正是这两种情况的结合才使希尔排序效率比插入排序高很多。
Shell算法的性能与所选取的分组长度序列有很大关系。
时间复杂度:
一般认为希尔排序的时间复杂度为O(n^1.5),比直接插入法要好。
希尔排序对于中等规模(n<=1000)的序列具有较高的效率。
#include <iostream>
using namespace std;
//对记录数组r做一趟希尔排序,length为数组的长度,delta为增量
void ShellInsert(int *r,int length,int delta)
{
int j;
//1 + delta为第一个子序列的第二个元素的下标
for(int i = delta + 1;i <= length;i++)
{
if(r[i] < r[i - delta])
{
r[0] = r[i]; //备份r[i],不做监视哨
for(j = i-delta; j>0&&r[0]<r[j]; j -= delta)
{
r[j + delta] = r[j];
}
r[j + delta] = r[0];
}
}
}
//对记录数组r做希尔排序,length为数组r的长度
//delta为增量数组,n为delta[]的长度
void ShellSort(int *r,int length,int *delta,int n)
{
for(int i = 0;i <= n-1;i++)
{
ShellInsert(r,length,delta[i]);
}
}
int main()
{
int a[] = {0,48,62,35,77,55,14,18};
int delta[]={2,1};
ShellSort(a,7,delta,2);
for(int i = 1;i <= 7;i++)
cout << a[i] << " ";
}