2022-09-06 15:33

算法:希尔排序

少尉

其它

(437)

(0)

收藏

希尔排序和插入排序很相似,有点像插入排序的升级版本。

希尔排序是希尔(Donald Shell)于1959年提出的一种排序算法。希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序,同时该算法是冲破O(n2)的第一批算法之一。

希尔排序也是一种插入排序算法,只不过在插入排序上突破了结界,达到了另一种顶峰的存在,这种顶峰使得时间复杂度变成「O(nLogn)~O(n^2)」。

希尔排序是通过分组+插入。

排序步骤:

这里有8个数据,分成4组,每两个进行比较,

比较完毕之后进行缩小增量操作,4/2 =2所以,数据又被分成两组

然后对上面的两组再分别进行直接插入排序,这样整个数组就更有序写,最后再缩小增量2/2=1,这样整个数组就为1组,直接进行简单的插入排序

最后数组就有序了。

实现代码:

public static void main(String[] args) {
int[] arr = {8,9,1,7,2,3,5,4,6,0};
        //希尔的第一轮排序
        //因为希尔是第一轮排序,所以分成5组
        for (int i = 5; i <arr.length; i++) {
            //遍历各组中的所有的元素(共有五组,每一组有两个元素,所以步长是5)
            for (int j =i-5 ; j >=0 ; j-=5) {
                //如果当前的元素大于加上步长后的那个元素,说明需要交换
                if (arr[j]>arr[j+5]){
                    int temp = arr[j];
                    arr[j] = arr[j+5];
                    arr[j+5] = temp;
                }
            }
        }
        System.out.println("希尔排序一轮后:");
        for(int i=0;i<arr.length;i++) {
        System.out.print(arr[i]+" ");
        }
        System.out.println();
        //希尔的第二轮排序
        for (int i = 2; i <arr.length; i++) {
            for (int j =i-2 ; j >=0 ; j-=2) {
                //如果当前的元素大于加上步长后的那个元素,说明需要交换
                if (arr[j]>arr[j+2]){
                int temp = arr[j];
                    arr[j] = arr[j+2];
                    arr[j+2] = temp;
                }
            }
        }
        System.out.println("希尔排序二轮后:");
        for(int i=0;i<arr.length;i++) {
        System.out.print(arr[i]+" ");
        }
        System.out.println();
        //希尔的第二轮排序
        for (int i = 1; i <arr.length; i++) {
            for (int j =i-1 ; j >=0 ; j-=1) {
                //如果当前的元素大于加上步长后的那个元素,说明需要交换
                if (arr[j]>arr[j+1]){
                int temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }
            }
        }
        System.out.println("希尔排序三轮后:");
        for(int i=0;i<arr.length;i++) {
        System.out.print(arr[i]+" ");
        }
        System.out.println();
}

运行结果:

image.png

以上就是推导过程,逐步分析可以知道:

public static void main(String[] args) {
int[] arr = {8,9,1,7,2,3,5,4,6,0};
        int count = 0;
        for (int gap = arr.length/2; gap > 0; gap=gap/2) {
            for (int i = gap; i <arr.length; i++) {
                //遍历各组中的所有的元素(共有gap组,每一组有两个元素,所以步长是gap)
                for (int j =i-gap ; j >=0; j-=gap) {
                    //如果当前的元素大于加上步长后的那个元素,说明需要交换
                    if (arr[j]>arr[j+gap]){
                        int temp = arr[j];
                        arr[j] = arr[j+gap];
                        arr[j+gap] = temp;
                    }
                }
            }
            System.out.println("希尔排序第"+(++count)+"轮后:");
            for(int i=0;i<arr.length;i++) {
            System.out.print(arr[i]+" ");
            }
            System.out.println();
        }
}

运行结果:

image.png

这里用的是交换法,效率不是特别高(可以在运行前和运行后统计一下时间),下面我们用移动法优化一下:

public static void main(String[] args) {
int[] arr = {8,9,1,7,2,3,5,4,6,0};
for (int gep = arr.length/2; gep > 0; gep=gep/2) {
         //从第gep和元素,逐个对其所在的组进行直接插入排序
         for (int i = gep; i <arr.length ; i++) {
             int j = i;//保存待插入的下标
             int temp = arr[j];//保存待插入的值
             if (arr[j]<arr[j-gep]){
                 while(j-gep>=0&&temp<arr[j-gep]){//如果说下标减去步长还是大于0的,并且待插入的数还是小于这个前一个步长的数,执行以下操作
                     //移动
                     arr[j] = arr[j-gep];//上述条件,满足后,就开始进行赋值,也就是移动操作
                     j-=gep;//然后gep步长--
                 }
                 //当退出while循环后,就给temp找到了位置
                 arr[j] = temp;
             }
         }
}
System.out.println("希尔排序后:");
for(int i=0;i<arr.length;i++) {
        System.out.print(arr[i]+" ");
     }
}

运行结果:

image.png

0条评论

点击登录参与评论