希尔排序是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为递减增量排序算法,同时该算法是冲破 O(n2) 的第一批算法之一。
希尔排序的基本思想是:先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录 “基本有序” 时,再对全体记录进行依次直接插入排序。
一.算法步骤
我们来看下希尔排序的基本步骤,在此我们选择增量 gap=length/2,缩小增量继续以 gap=gap/2 的方式,这种增量选择我们可以用一个序列来表示,{n2,(n/2)2,…,1},称为增量序列。希尔排序的增量序列的选择与证明是个数学难题,我们选择的这个增量序列是比较常用的,也是希尔建议的增量,称为希尔增量,但其实这个增量序列不是最优的。此处我们做示例使用希尔增量。
假设这里有一个长度为10的数组arr:
- 首先他整体上还是先局部后整体的排序方案,只不过其分组方式有点特殊而已。用的是增量gap来去分组的。首先gap=10/2=5,第一次分组按照分割为5进行分组,即0与5,1与6,2与7....一共分成了5组。
- 分好组后就会进行组内排序,如何排序呢?进入一个循环,以gap为起点开始,此时就是从数组中间开始的,先记录gap位置上的元素cur,然后再找到与之一组的元素,减去gap即可得到,然后比较两者,大者在后面并进行交换。此时数组只有两个元素而已,如果有更多那就继续向前找到元素并比较,将比cur较大者逐步向前挪移,最后循环结束,将cur放置在当前小组的停止位置上。
- 然后继续分组,此时gap=gap/2=2,即以02468,13579为两组,然后继续进行组内排序。
- 最后gap=1时候,此时只有一组,那就是整个数组,然后再次进行插入排序,完成排序。
public class demo {
public static void main(String[] args) {
int[] nums = {7, 3, 2, 5, -6, -1, 4, 4, 9, 8};
int[] ints = shellSort(nums);
for (int i = 0; i < ints.length; i++) {
System.out.print(ints[i] + " ");
}
}
public static int[] shellSort(int[] arr) {
int n = arr.length;
int tmp = n / 2;
while (tmp >= 1) {
for (int i = tmp; i < n; i++) {
int cur = arr[i];
int pre = i - tmp;
while (pre >= 0 && arr[pre] > cur) {
arr[pre + tmp] = arr[pre];
pre -= tmp;
}
arr[pre + tmp] = cur;
}
tmp /= 2;
}
return arr;
}
}
二.算法分析
- 稳定性:不稳定
- 时间复杂度:最佳:O(nlogn), 最差:O(n2) 平均:O(nlogn)
- 空间复杂度:O(1)