希尔排序(Shell's Sort)是插入排序的一种又称“缩小增量排序”(Diminshing Increment Sort),是直接插入排序算法的一种更高效的改进版本。该方法因D.L.Shell于1959年提出而得名。
希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的数据元素越来越多,当增量减至1时,整个序列恰被分成一组,算法便终止。
希尔排序算法思想:
希尔排序是一种先按照一定增量进行分组,然后对各小组中所有的元素进行直接插入排序然后形成一个新的序列。之后减少增量,然后对新的序列再分组以及直接插入排序。直到增量为1的时候,对整个序列进行一次直接插入排序。
希尔排序算法举例:
例如,现在要对序列{ 1,8,3,9,5,7 }进行希尔排序。
- 第一步,选择增量为3,进行排序:
则产生三个分组(红括号为数组元素下标)
{ 1(0),9(3) }
{ 8(1),5(4) }
{ 3(2),7(5) }
然后对该所有分组进行直接插入排序,得到新的分组(注意:此时下标是不会变的,只是用直接插入排序进行数组排序)
{ 1(0),9(3) }
{ 5(1),8(4) }
{ 3(2),7(5) }
因此按照下标,我们可以写出第一趟按照增量为3的希尔排序得到的序列:{ 1,5,3,9,8,7 }
- 第二步,选择增量为1,进行排序
这一步,直接按照直接插入排序的方法,进行直接插入排序,得到有序序列:{ 1,3,5,7,8,9 }
代码实现:
这是本人摸索写出来的希尔排序,是按照一定算法设置增量。和网上蛮多版本不同。
#include<stdio.h> //希尔排序 void ShellSort(int a[], int length) { int i, j, temp; int h = 1;//增量 //控制最大增量 if (h < length / 3) h = length / 3 + 1; while (h > 0) { for (i = 0; i < length; i++) { temp = a[i]; for (j = i; j >= 1 && a[j - h] > temp; j -= h) { a[j] = a[j - h]; } a[j] = temp; } printf("增量为%d之后排序为:", h); printArr(a, length); //递减增量 h /= 2; } } void main() { int aa[] = { 1,8,3,9,5,7 }; //int n = sizeof(aa) / sizeof(aa[0]); //printf("数组序列长度为:%d 数组序列为:", n); //printArr(aa, n); //printf("开始希尔排序: "); //ShellSort(aa, n); printf("开始希尔排序: "); ShellSort(aa, 6); getchar(); }
运行结果如下图所示
下面的实现代码是网上的通用的实现方法,只不过这个增量h的值却搞死了,只能是先( h = 数列的长度2 ),然后一直除以2进行递减h。
void shell_sort(int array[], int length) { int i; int j; int k; int gap; //gap是分组的步长 int temp; //希尔排序是在直接插入排序的基础上实现的,所以仍然需要哨兵 for (gap = length / 2; gap > 0; gap = gap / 2) { for (i = 0; i < gap; i++) { for (j = i + gap; j < length; j = j + gap) { //单独一次的插入排序 if (array[j] < array[j - gap]) { temp = array[j]; //哨兵 k = j - gap; while (k >= 0 && array[k] > temp) { array[k + gap] = array[k]; k = k - gap; } array[k + gap] = temp; } } } } printArr(array, length); }
遗留问题:
本人使用我自己书写的算法代码调试的时候发现了一个挺怪异的问题,
就是当我求数组元素的时候,数组长度值n就会把数组中的一个元素替换掉!!!
但是如果函数参数直接是写出数组长度,则正常
本人接触C语言时间不长,算法逻辑应该是没错的,到底是哪的问题呢?还请各位大佬赐教。
时间复杂度:
在最优的情况下,时间复杂度为:O(n ^ (1.3) )
在最差的情况下,时间复杂度为:O(n ^ 2)
稳定性:
希尔排序是一种不稳定的排序算法