shell排序法也运用到了直接插入排序法,因此重点讲下。
算法原理
原始的算法实现在最坏的情况下需要进行O(n2)的比较和交换。V. Pratt的书对算法进行了少量修改,可以使得性能提升至O(nlog2n)。这比最好的比较算法的O(nlogn)要差一些。
希尔排序通过将比较的全部元素分为几个区域来提升插入排序的性能。这样可以让一个元素可以一次性地朝最终位置前进一大步。然后算法再取越来越小的步长进行排序,算法的最后一步就是普通的插入排序,但是到了这步,需排序的数据几乎是已排好的了(此时插入排序较快)。
假设有一个很小的数据在一个已按升序排好序的数组的末端。如果用复杂度为O(n2)的排序(冒泡排序或插入排序),可能会进行n次的比较和交换才能将该数据移至正确位置。而希尔排序会用较大的步长移动数据,所以小数据只需进行少数比较和交换即可到正确位置。
一个更好理解的希尔排序实现:将数组列在一个表中并对列排序(用插入排序)。重复这过程,不过每次用更长的列来进行。最后整个表就只有一列了。将数组转换至表是为了更好地理解这算法,算法本身仅仅对原数组进行排序(通过增加索引的步长,例如是用i +=step_size而不是i++)。
例子:
算法:
/*
**直接插入排序
*/
void InsertSort(int a[], int len)
{
int i, j, key;
for(i = 1; i < len; ++i){
key = a[i];
for(j = i-1; j >=0; --j){
if(a[j] > key)
a[j+1] = a[j];
else
break;
}
a[j+1] = key;
}
}
错误的写法:
void insersort(int a[], int n){
int tmp = 0;
for (int i = 1;i< n;i++)
{
for (int j = i;j > 0; j--)
{
tmp = a[j];
while(a[j] > a[i]){
a[j] =a [j--];
}
a[j] = tmp;
}
}
}
/*
*D.Shell最初的算法。
*/
int shellsortSh(int p[],int n)
{
int op=0;
int h,i,j,temp;
for(h=n/2;h>0;h=h/2){
for(i=h;i<n;i++){
temp=p[i];
for(j=i-h;j>=0&&p[j]>temp;j-=h){
p[j+h]=p[j];
op++;
}
p[j+h]=temp;
op++;
}
}
return op;
}