排序算法-希尔排序
方法
希尔排序是基于插入排序实现的,之前的博客分析过,插入排序在数组基本有序的时候,也就是倒置元素数量尽可能少的时候效率很高。
希尔排序的思想就是,先在整个数组中划分出规模比较小的数组,将这些小的数组先进行插入排序,将小的数组变得有序。在将小的数组变得有序之后,整个数组的倒置元素数量变少了,此时再进行插入排序的效率也变高了。因此我需要先分成若干个规模小一点的数组,进行插入排序,之后再划分成若干个大一点规模的数组进行排序…直到划分成最终一整个数组,这样我们最终就能得到一个有序的数组了。
具体步骤:
假设数组 arr 有n个无序元素。
设定一个划分间隔 h
首次划分:
将 arr[0] arr[h] arr[2h] … 划为一个数组
将 arr[1] arr[h+1] arr[2h+1] … 划为一个数组
…
将 arr[h-1] arr[2h-1] arr[3h-1] … 划为一个数组
将这些数组划分好了之后,对其插入排序,因为 h 一开始会设定成一个较大的数,所以这些数组规模较小,用插入排序可以很快的完成排序。此时数组已经减少了很多倒置元素。
完成首次划分之后,将 h 变小再次进行划分排序,不断地进行这个循环。每一次划分排序都会减小数组的无序性,也就是说倒置元素会越来越少,直到 h 变为 1 的时候,数组只能划分成一整个原数组。此时再对基本有序的原数组进行排序,效率就非常高了。
实现
@Override
public void sort(int[] arr) {
int length = arr.length;
int h = 1;
while (h < length/3)
h = 3*h + 1;
while (h >= 1) {
insertsort(arr, h);
h = h/3;
}
}
private void insertsort(int[] arr, int h) {
for (int i = 0; i < arr.length; i += h) {
int n = arr[i];
int loc = i;
for (int j = i; j > 0; j -= h) {
if (n >= arr[j-h]) break;
arr[j] = arr[j-h];
loc = j-h;
}
arr[loc] = n;
}
}
复杂度分析
因为是基于插入排序的算法,因此在数组倒置元素越少的情况下,该算法效率也会越高。
时间复杂度
水平不足分析出时间复杂度,就直接放别人的结果了:O(n^(1.3—2))
空间复杂度
在原数组的空间上进行交换,所以空间复杂度为 O(1)。