插入排序----希尔排序-----(本文给出了另一种直接插入排序)

希尔排序原理如下(纯手打):

假设有下面一个数组

99, 38, 65, 82, 26, 13, 27, 49, 55, 1

第一趟排序

首先设置一个增量inc,比如说最开始的增量为数组长度的一半即 arr.length/2 = 5,则上面的数组可以分成如下几组(相同颜色的为一组):
这里写图片描述

将每组中的元素进行比较,小的放在前面,大的放在后面(即进行直接插入排序)则第一趟排序后的数组为
这里写图片描述

第二趟排序

将增量设置为第一趟排序时增量的一半,即inc此时为5/2 = 2,则上面的数组可以分成如下几组(相同颜色的为一组)
这里写图片描述
然后将分好组的数据分别按照直接插入排序的方式进行比较,排序后的结果为
这里写图片描述

第三趟排序

将增量设置为第一趟排序时增量的一半,即inc此时为2/2 = 1,则上面的数组可以分成如下几组(相同颜色的为一组)
这里写图片描述
此时数组变为1组(基本有序),对这一组进行直接插入排序,得到的结果就是原始数组排序后的数组:
这里写图片描述

总结一下:

由上面的原理介绍可以看出:
①当增量为inc时,共可以分成inc-1个小组,且每个小组的第一个元素分别为0,1,2…,inc-1,inc
②在每个小组内其实就是分别进行了直接插入排序
③该算法的终止条件为inc>0或者说inc = 1

结论4是代码2的理论基础

④从索引inc开始,之后的每一个数都至少是所在小组中的第二个数,且inc是第一小组的第2个,inc+1是第二小组(如果有的话)的第二个…..所以遍历inc及其之后的每一个数时,都可以通过该数所在索引i找到该小组中前面的数,分别为i-1*inc,i-2*inc….i-n*inc直到i-n*inc>=0时为止.由此可以通过对比遍历到的数arr[i]与前面已经排好序(这里一定要注意)的arr[i-1*inc],arr[i-2*inc]….,arr[i-n*inc]进行一一比较,如果遍历到的数字arr[i]小于前面的数,前面的数就往后移动inc个位置,直到找到一个不大于arr[i]的数,也就找到了arr[i]应该插入的正确位置.

按照上诉思路我写出的代码如下:

代码1:

package cn.nrsc.sort;

public class ShellSort {
    public static void main(String[] args) {
        int[] arr = { 49, 38, 65, 97, 23, 22, 76, 1, 5, 8, 2, 0, -1, 22 };

        shellSort(arr);

        System.out.println("排序后:");
        for (int i : arr) {
            System.out.println(i);
        }
    }

    private static void shellSort(int[] arr) {

        int inc = arr.length / 2;// 初始增量
        while (inc > 0) {
            for (int i = 0; i < inc; i++) { // 拆分后数组的首个元素
                for (int j = i; j < arr.length; j += inc) {//遍历获得以i为首个元素的小组

                    //下面其实就是直接插入排序算法

                    int k;
                    int tmp = arr[j];// 新遍历的值等待插入到前面的有序数组中

                    // 通过下面的循环将比tmp大的数字往后移inc位,并找到不比tmp大的数字的下标j
                    // 因为比tmp大的数字都往后移了一位,则第j+inc位的数字移动到了j+2inc位,j+inc位给空出来了
                    // 并且由该算法的思想可知,tmp正好应该放在j的后inc位,即j+inc位
                    for (k = j - inc; k >= i; k -= inc) {
                        if (arr[k] > tmp) {
                            arr[k + inc] = arr[k];
                        } else {
                            break;
                        }

                    }
                    arr[k + inc] = tmp;

                }
            }
            inc /= 2; // 增量减半
        }
    }
}

代码2:

package cn.nrsc.sort;

public class ShellSort {
    public static void main(String[] args) {
        int[] arr = { 49, 38, 65, 97, 23, 22, 76, 1, 5, 8, 2, 0, -1, 22 };

        shellSort(arr);

        System.out.println("排序后:");
        for (int i : arr) {
            System.out.println(i);
        }
    }

    private static void shellSort(int[] arr) {
        // 控制增量
        for (int inc = arr.length / 2; inc > 0; inc /= 2) {
            // 从按照增量所分小组的第2个数开始进行遍历----inc为第一小组的第2个数,inc+1为第2小组(如果有的话)的第二个数....
            for (int i = inc; i < arr.length; i++) {

                int tmp = arr[i];// 新遍历的值等待插入到前面的有序数组
                int j = i;  // 这里之所以要将i赋给一个新的变量,
                            // 是因为在下面的循环中要通过该变量的变化,来找到arr[i]之前的数组
                // 通过下面的循环①将大于tmp的数都向后移动了inc位,②找到tmp需要插入的位置j
                // tmp的位置是j是因为先用arr[j-inc]与tmp进行了比较
                while (j - inc >= 0 && arr[j - inc] > tmp) {
                    arr[j] = arr[j - inc];
                    j = j - inc;
                }
                arr[j] = tmp;

            }
        }
    }

}

由总结4还可以得到另一种形式的直接插入排序算法:

package cn.nrsc.sort;

public class InsertSort {
    public static void main(String[] args) {
        int[] arr = { 49, 38, 65, 97, 23, 22, 76, 1, 5, 8, 2, 0, -1, 22 };

        insertSort(arr);

        System.out.println("排序后:");
        for (int i : arr) {
            System.out.println(i);
        }
    }

    private static void insertSort(int[] arr) {
        // 从数组中的第二个数开始往前看
        for (int i = 1; i < arr.length; i++) {

            int tmp = arr[i];// 新遍历的值等待插入到前面的有序数组
            int j = i;  // 这里之所以要将i赋给一个新的变量,
                        // 是因为在下面的循环中要通过该变量的变化,来找到arr[i]之前的数组

            //通过下面的循环①将大于tmp的数都向后移动了一位,②找到tmp需要插入的位置j
            //tmp的位置是j是因为先用arr[j-1]与tmp进行了比较
            while (j - 1 >= 0 && arr[j - 1] > tmp) {
                arr[j] = arr[j - 1];
                j = j - 1;
            }
            //将tmp插入到位置j
            arr[j] = tmp;

        }
    }

}

最后说一点

希尔排序的最差情况下的时间复杂度为Θ(N²),即最好最坏情况下都是N²,比如说对于下面的数组,前几个增量内的循环没起到任何作用,只有增量为1时才起到了排序的效果,则前几个增量相当于白跑了好几趟—-具体的更深入的研究大家可以自行探索.
这里写图片描述

猜你喜欢

转载自blog.csdn.net/nrsc272420199/article/details/82526258