算法思想:(缩小增量排序)
将一个序列分成多个子序列,然后每个子序列进行直接插入排序,其中增量的选取控制着子序列的生成。如果增量是1,就是直接插入排序。
相比前两种插入排序:
希尔排序的每趟排序都会使整个序列变的更加有序,多次排序,效率更高。
缺点:
希尔排序是不稳定排序。因为分割组序列进行排序,所以会存在相同大小元素,位置颠倒的情况。
例子:
1.如先以增量为5来分割序列:即将下标为0、5、10、15、...的关键字分成一组;将下标为1、6、11、16、...的分成一组;然后对各组进行直接插入排序,这就是第一遍希尔排序。
2.再以增量为2来分割序列:即将下标为0、2、4、6...的元素分成一组,将下标为1,3,5,7,9...的元素分成一组,各组进行直接插入排序,这又是一趟希尔排序。
3.最后以增量为1,来进行一次直接插入排序,完成排序。
增量的选取
希尔自己提出的是:n/2, n/4 , n/8, ... , n/(2^k), .... , 2 , 1。即每次将增量除以2并向下取整,其中n为序列的长度,此时时间复杂度为O(n^2)
帕佩尔洛夫和斯塔舌维奇提出:2^k+1, ... ,65, 33, 17, 9, 5, 3, 1。其中k是大于等于1的整数, 2^k+1小于序列长度,增量序列末尾的1是额外添加的。此时时间复杂度为O(n^1.5)
希尔排序特点:
增量序列的最后一个值一定取1
增量序列中的值应尽量没有除1之外的公因子。
Coding
#include<stdio.h>
#include<math.h>
//数组输出函数
void printarr(int arr[],int k)
{
for(int i=0;i<k;i++)
{
printf("%d ",arr[i]);
}
printf("\n");
}
//希尔排序
void xierSort(int arr[],int k)
{
int i,j,temp,t;
int gap=1;
int n=0;
//计算递增序列:1 3 5 9 17 33 ... 2^k+1,k为大于1的整数,2^k+1<待排序序列的长度
while(gap<k)
{
n++;
gap=pow(2,n)+1;
}
gap=gap/2+1;
printf("%d\n",gap);
while(gap >= 1)
{
for (i = gap; i < k; i += gap) { //0-gap假设为已经是排好序的多个初始序列(每个序列只有一个值)
t = i;
temp = arr[t];
//直接插入排序:直接向前比较大小,如果序列前面的值,大于它,则比较的元素后移gap位,它继续向前寻找比较,知道达到初始位置0或找到比自己小的元素,
for (j=i-gap; j >= 0 && arr[j] > temp ; j -= gap) {
arr[j + gap] = arr[j];
t = j;
}
arr[t] = temp; //插入元素
//gap序列调整
if(gap==3)
{
gap=1;
}
else if(gap==1)
{
gap=0;
}
else
{
gap=gap/2+1;// 3 5 9 17 33 ...
}
printf("%d\n",gap);
}
printarr(arr,k);
}
int main()
{
int arr1[20]={10,7,8,6,5,4,2,3,1,9,4,55,22,11,22,6,8,1,9,4};
xierSort(arr1,20);
}
希尔排序空间复杂度:O(1)