希尔排序
希尔排序(Shell's Sort)是插入排序的一种又称“缩小增量排序”(Diminishing Increment Sort),是直接插入排序算法的一种更高效的改进版本,同时该算法是冲破O(n2)的第一批算法之一,是非稳定排序算法。该方法因 D.L.Shell 于 1959 年提出而得名。
基本思想
算法先将要排序的一组数按某个增量d分成若干组,每组中记录的下标相差d.对每组中全部元素进行排序,然后再用一个较小的增量对它进行分组,在每组中再进行排序。当增量减到1时,整个要排序的数被分成一组,排序完成。
增量
先取一个小于n的整数d1作为第一个增量,把文件的全部记录分组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2...。一般的初次取序列的一半为增量,以后每次减半,直到增量为1。假设待排序数组array长度为10:49,38,65,97,76,13,27,49,55,04。 那么增量d = array.length / 2,取值依次为5,2,1。
增量d=5
增量为5,将待排序数组array分成5组子序列,分别是[14,13]、[38,27]、[65,49]、[97,55]、[76,04]。 子序列中使用插入排序。 排序后的结果:
增量d=2
增量为2,将d=5排序后的结果分成2组子序列,分别是[13,49,04,38,97]、[27,55,49,65,76]。缩小增量后进行插入排序。 排序后的结果:
增量d=1
当增量为1时,则希尔排序已进行至最后一步,元素之间依然使用插入排序。 排序结果:
代码实现
最坏的情况:长度为100,0000的倒序整型数组为例进行排序。
希尔排序
- 初始化数据
/**
* 初始化数据
* @param length 数组长度
* @return 数据
*/
private static int[] initData(int length) {
int[] array = new int[1000000];
for (int i = 1000000; i > 0; i--) {
array[1000000 - i] = i;
}
return array;
}
- 计算增量进行插入排序
/**
* 希尔排序
*/
private static void shellSort() {
// 获取待排序数据
int[] array = initData(1000000);
// 希尔排序开始时间
long startTime = System.currentTimeMillis();
// 计算增量
int gap = array.length;
while (true) {
// 增量每次减半
gap /= 2;
for (int i = 0; i < gap; i++) {
// 插入排序
for (int j = i + gap; j < array.length; j += gap) {
int k = j - gap;
// 若增量前元素大于增量后元素则交换位置
while (k >= 0 && array[k] > array[k + gap]) {
// 交换位置
int temp = array[k];
array[k] = array[k + gap];
array[k + gap] = temp;
// 开始增量下一个元素
k -= gap;
}
}
}
if (gap == 1)
break;
}
// 希尔排序结束时间
long endTime = System.currentTimeMillis();
System.out.println("希尔排序时间:" + (endTime - startTime) + "毫秒");
}
插入排序
- 初始化数据。为了和希尔排序比较排序速度,排序数组保持一致。
- 插入排序。
/**
* 插入排序
*/
private static void insertSort() {
// 获取待排序数据
int[] array = initData(1000000);
// 插入排序开始时间
long startTime = System.currentTimeMillis();
int l = array.length;
for (int i = 1; i < l; i++) {
// 若前面元素大于后面元素则交互位置
for (int j = i; j > 0 && (array[j] < array[j - 1]); j--) {
int temp = array[j];
array[j] = array[j - 1];
array[j - 1] = temp;
}
}
// 插入排序开始时间
long endTime = System.currentTimeMillis();
System.out.println("插入排序时间:" + (endTime - startTime) + "毫秒");
}
排序比较
由上图中排序时间可以看出:对长度为100,0000的int倒序数据进行升序(最坏情况),希尔排序时间远小于插入排序时间。
总结
- 希尔排序是非稳定排序算法
- 希尔排序是基于插入排序的一种算法。
- 希尔排序最坏时间复杂度为O( n^3/2^ )
- 希尔排序最后一个增量必须为1
- 希尔排序的时间性能优于直接插入排序
- 希尔排序在中等大小规模表现良好,对规模非常大的数据排序不是最优选择