算法|希尔排序

理解希尔排序前,请务必先理解 插入排序,这是前提!

插入排序的思路就是在未排序的数组中选择第一个元素,在已经排序的数组中找到合适的位置并插入.他相比选择排序的优势在于,如果数组部分有序,或者大多数元素理最终的位置都不太远,就可以大大减少比较和交换次数.

但是存在这样情况,比如最小的元素恰好在数组的最末尾,它需要依次和相邻的元素比较和交换,所以我们需要进行需要N-1次比较和N-1次交换位置,才能将最小元素移动到数组的另一端.这个过程近乎选择排序,对与插入排序来说,有没有更好的办法来提高排序速度,减少这种移动呢?答案就是希尔排序

在插入排序的过程中,元素的比较交换都是发生在相邻的元素中,而希尔排序可以一次跨越多个元素,在插入排序中,我们是跨度是1,在希尔排序中我们的跨度是h,实际上,希尔排序就是对插入排序的优化,在插入排序中我们说过插入排序的三种复杂度情况,并且插入排序适合部分元素有序,因为部分有序数组可以大大减少交换和比较的次数,那么希尔排序就是用来构建部分有序数组,从而为更小跨度的插入排序准备条件,以加快插入排序的速度

在插入排序中,元素的比较和交换的都是发生在相邻元素中的,操作跨度为 1,希尔排序的实际上就是不断的重复插入排序的过程,但是跨度从h开始,然后不断的递减,最后减小到1,当跨度为1时,这时候就是普通的插入排序了.

换句话说,希尔排序就是起点和跨度不同的插入排序,在每个跨度为h的子数组中,执行插入排序,并且h在不断的变小,变小的方式为 h=h/3:


当h=13时,将每个元素间隔为13的子数组进行插入排序,插入排序在所有间隔为13的子数组插入排序结束后,按照h=h/3

当h=4时, 将每个元素间隔为4的子数组进行插入排序,插入排序在所有间隔为4的子数组插入排序结束后,按照h=h/3

当h=1时,就是普通的插入排序了

之前我们说,当数组部分有序时,插入排序的优势时相当大的,而我们进行不同跨度的插入排序,就是为跨度为1的插入排序做准备.

当h减小到1时,其实数组已经是部分有序的了

插入排序的算法实现为:

public class ShellSort {
    public static void main(String[] args) {
        int[] nums = new int[]{1, 15, 3, 78, 34, 23, 46, 2, 8, 34, 57};
        System.out.println(Arrays.toString(nums));
        sort(nums);
        System.out.println(Arrays.toString(nums));
    }

    public static void sort(int[] arrays) {
        int n = arrays.length;

        int h = 1;
        while (h < n / 3) h = 3 * h + 1;

        while (h >= 1) {

            for (int i = h; i < n; i++) {
                int j = i;
                for (; j >= h; j = j - h) {
                    if (arrays[j] < arrays[j - h]){

                        int temp = arrays[j];
                        arrays[j] = arrays[j-h];
                        arrays[j -h] = temp;
                    }
                }
            }

            h = h / 3;
        }


    }
}

实际上上面这种写法的性能并不高,真正的希尔排序应该是下面这样的:

    public static void sort(int[] arrays) {
        int n = arrays.length;

        int h = 1;
        while (h < n / 3) h = 3 * h + 1;

        while (h >= 1) {

            for (int i = h; i < n; i++) {

                for (int j = i; j >= h && arrays[j] < arrays[j - h]; j = j - h) {

                    int temp = arrays[j];
                    arrays[j] = arrays[j - h];
                    arrays[j - h] = temp;
                }
            }

            h = h / 3;
        }

    }

将条件比较语句放在 for 循环内部!

希尔排序的过程为:


图中的高亮元素就是发生比较和交换操作的元素,可以对比下普通插入排序

到现在为止,还没有对选择排序,插入排序,希尔排序进行大规模的数据排序性能验证,下一篇,将使用十万,百万级别的数组进行排序性能检测

猜你喜欢

转载自blog.csdn.net/l491337898/article/details/79911547