希尔排序是在插入排序的基础上进行的。
主要思想是将排序的元素按照一定步长进行分组,对每组中的元素按步长进行插入排序,进行完第一次各组排序后的结果再比上次小的步长,再分组,再各组逐次排序,再得到排序结果..., 直到这个步长变为1的时候,其实就是真正的插入排序。这次完全按照插入排序算法对所有元素排序。得到结果后。整个排序结束。
先是分组12个元素,元素个数/2=6,所以第一次分组步长为6,分得6组如下(因为后面会依组逐次排序,所以这个组中的元素是动态变化,并不完全如图示,不过对这6个区域的划分应该是这样的):
然后对第一组按步长插入排序:
对6组依组逐次排序(第一组按步长插入排序完后得到一个结果,然后第二组是从这个结果上以第二个元素开始,按步找到第二组所有元素,再按步长插入排序,第三组是在第二组排完序的结果上,按步长找出第三组元素,再按步长插入排序。。。直到6组都排完序)
同样的道理,第二次分组步长减半(6/2=3), 按步长3分组,排序也是依组的顺序逐次排序(和按6为步长排序一样):
最后步长是3/2=1(此处是整除),也就是真正插入排序了(黄色标记部分为需要插入时的插入点,蓝色圆圈代表待插入元素,具体是否需要插入需要和红色中括号内的元素从右到左做完比较才能确定):
希尔排序实现代码:
#include <stdio.h>
void PrintArray(int a[],int n)
{
int i;
for(i=0;i<n;i++)
printf("%d ", a[i]);
}
void ShellSort(int a[], int arr_len)
{
/*用希尔排序算法对a[0]--a[arr_len-1]排序,arr_len为数组的长度*/
int i,j,k,span;
int temp;
int m = 0;
/*span是步长,最后值会变成1*/
span = arr_len/2;
while(span!= 0)
{
m++;
printf("第%d次步长span:%d,",m,span);
for(k=0;k<span;k++)
{
/*组内是直接插入排序,区别是每次不是增1而是增span*/
for(i=k;i<arr_len-span;i=i+span)
{
/*临时变量寄存每组最后一个元素*/
temp = a[i+span];
j = i;
/*a[j]相当于每组首个元素*/
while(j>-1&&temp<=a[j])
{
a[j+span] = a[j];
/*此处j-span是为了方便循环外紧接着的一句
如果该循环体被执行到了,那么每组首个元素比
该组最后一个元素大,该组首个元素的值被赋给了
最后一个元素,循环外则需要将早先记住的
该组最后一个元素值赋给首个元素
*/
j = j-span;
}
/*如果上面的循环体被执行,则此处相当于把
每组最后一个元素值赋给首个元素,
否则就是改组最后一个元素再赋予一次旧值*/
a[j+span] = temp;
}
}
span/=2;
printf("分组排序结果:");
PrintArray(a, arr_len);
printf("\n");
}
}
int main()
{
int a[]={65,34,25,87,12,38,56,46,14,77,92,23};
int arr_len = sizeof(a)/sizeof(a[0]);
ShellSort(a, arr_len);
printf(" 希尔排序的最终结果:");
PrintArray(a, arr_len);
return 0;
}
程序运行结果(开始的排序图中红色粗线框所圈的三个部分是三次排序结果):