插入排序算法-希尔排序

算法简介

希尔排序也是插入排序的一种,更准确的说是插入排序的升级版,在插入排序前加上了一些操作使得元素先基本有序之后再进行插入排序。

希尔排序又称为缩小增量排序,字如其名,朴素的希尔排序算法是通过将增量 k 设为数组长度的一半,以后会每次缩小一半,最后以增量为 1 为结束,增量为 k 时,表示整个数组分为了 k 组,每隔 k-1 的元素为 1 组,比如 arr[0] 和 arr[k] 是一组的,然后我们对所有的组内的元素进行直接插入排序,使得每个组组内元素都是有序的,通过多轮组内排序后,元素已经基本有序了,最后经过最后一轮,最后一轮增量为 1,也就是 1 组,就是最常规的直接插入排序了。

希尔排序为什么要这么做,这是因为,直接插入排序在数组基本有序的情况下效率是最高的,希尔排序为了达到基本有序的目前,通过缩小增量来实现让元素基本有序

希尔排序时间复杂度要依据缩小增量的取值来定的,下方算法中采用的是朴素希尔排序缩小增量的取值,为 O(n^1.3-2),空间复杂度为 O(1),一般来说希尔排序是不稳定的排序算法

时间复杂度 O(n^2) 和直接插入排序一样,空间复杂度 O(1),是稳定的排序算法

Java 实现

思路

希尔排序采用缩小增量的思路,我们大致可以将思路分解为三个层级,第一层级也是最外层循环我们写缩小增量的轮次,第一次假如增量为 4,第二次增量为 2,最后一次增量为 1,第二层级也是第二层循环我们遍历以增量划分后的组数,有几组循环几次,因为我们只需要对组内元素进行排序,假如增量为 k,那么组数就是 k,每组中就有arr.length/k个元素,第三层我们写一个直接插入排序即可,但是此直接插入排序是要用到增量 k,而不再是常规的增量 1 了,这样的话套用了四层循环,最里头的两层是直接插入排序,外头的两层是为了调整为粗略有序,最外层多轮粗略有序调整,第二层只进行组内粗略调整

我们结合下面的代码,很多地方都把第二个循环和第三个循环揉在了一起,我觉这样思考非常不人性化,如果我拆成两个循环,表示先是依据组来遍历,有 k 个组,然后里头再是直接插入排序的东西,网上是合在一起,表示整个循环中可以轮着遍历各个组,比如当前 i = k,我让第一组整个插入到前面属于第一组的序列中,然后 i++,我让第二组的数插入前面只属于第二组的序列中,然后第三组,第四组,不断交替着来。而我的思想是严格分为 k 个组,每个组里头执行一个直接插入算法,执行 k 轮循环

代码实现

// 希尔排序
public int[] shellSort(int[] arr) {
    // 我们采取朴素希尔排序的增量
    for (int k = arr.length / 2; k >= 1; k/=2) {
        // 目前是 k 组,每组 arr.length/k 个数
        for (int i = 0; i <= k; i++) {
            // 直接插入排序
            for (int j = i + k; j <= arr.length; j+=k) {
                int temp = arr[j];
                for (int m = j - k; m >= 0; m-=k) {
                    if (arr[m] > temp) {
                        arr[m+k] = arr[m];
                    }
                    else {
                    	arr[m+k] = temp;
                        break;
                    }
                }
            }
        }
    }
    return arr;
}

时间复杂度

希尔排序时间复杂度要依据缩小增量的取值来定的,O(n^1.3-2)

空间复杂度

希尔排序的空间复杂度为 O(1)

算法稳定性

一般来说希尔排序是不稳定的排序算法

发布了197 篇原创文章 · 获赞 62 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/abcnull/article/details/104666906
今日推荐