数据结构与算法-------希尔排序------交换法+移动法

希尔排序

希尔排序是希尔(DonaldShell) 于1959年提出的一种排序算法。希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序。

基本思想

希尔排序是把记录按下标的一定增量分组对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止

解释

我们看常见的希尔排序动态图解

img

太快额?

来个静态图示

在这里插入图片描述

已经非常接近有序的了,即使需要移动来插入,也就移动1到2步,我们就可以使用直接插入排序对这一个大组进行排序

当然,我们对有序序列进行插入时,怎么插入的?

分别有**交换法,移动法,**我们分别写出来测一下

请听题

科大10个人去面试CSDN社团,满分是10分,现在给出的分数是8,9,1,7,2,3,5,4,6,0,请用希尔排序将他们从小到大排列

交换法推导过程

package com.wang.sort;

import java.util.Arrays;

/**
 * @author 王庆华
 * @version 1.0
 * @date 2020/12/24 17:11
 * @Description TODO
 * @pojectname 希尔排序算法
 */
public class ShellSort {
    
    
    public static void main(String[] args) {
    
    
        int[] arr = {
    
    8,9,1,7,2,3,5,4,6,0};
        ShellSort(arr);
    }
    //交换法逐步推导过程
    public static void ShellSort(int[] arr){
    
    
        int temp = 0;
        //希尔排序的第一轮排序
        //分组,我们例子是10个数据除以二就是5组
        for (int i = 5; i <arr.length ; i++) {
    
    
            //遍历各组中所有的元素,共有5组,每组2个元素
            //步长是5  5-5=0   6-5=1 7-5=2
            for (int j = i-5; j >=0 ; j-=5) {
    
    
                //如果当前元素大于加上步长后的元素,说明需要交换
                if (arr[j]>arr[j+5]){
    
    //0>6  1>7 2>8 3>9 4>10
                    temp = arr[j];
                    arr[j] = arr[j+5];
                    arr[j+5] = temp;
                }
            }
        }
        System.out.println("希尔排序第一轮后"+ Arrays.toString(arr));
        //第二轮
        //在第一组的基础上,5组再除以2 分为2组,步长变为2
        for (int i = 2; i <arr.length ; i++) {
    
    
            //遍历各组中所有的元素,共有5组,每组2个元素
            for (int j = i-2; j >=0 ; j-=2) {
    
    
                //如果当前元素大于加上步长后的元素,说明需要交换
                if (arr[j]>arr[j+2]){
    
    //0>6  1>7 2>8 3>9 4>10
                    temp = arr[j];
                    arr[j] = arr[j+2];
                    arr[j+2] = temp;
                }
            }
        }
        System.out.println("第二轮希尔排序后的结果"+Arrays.toString(arr));
        //第三轮
        //在第二组的基础上,2组再除以2 分为1组,步长变为1
        for (int i = 1; i <arr.length ; i++) {
    
    
            //遍历各组中所有的元素,共有5组,每组2个元素
            for (int j = i-1; j >=0 ; j-=1) {
    
    
                //如果当前元素大于加上步长后的元素,说明需要交换
                if (arr[j]>arr[j+1]){
    
    //0>6  1>7 2>8 3>9 4>10
                    temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }
            }
        }
        System.out.println("第三轮希尔排序后的结果"+Arrays.toString(arr));
    }
}

希尔排序交换法

/**
 * @author 王庆华
 * @version 1.0
 * @date 2020/12/24 17:11
 * @Description TODO
 * @pojectname 希尔排序算法
 */
public class ShellSort {
    
    
    public static void main(String[] args) {
    
    
        int[] arr = {
    
    8,9,1,7,2,3,5,4,6,0};
        ShellSort(arr);

    }
    //交换法逐步推导过程
    public static void ShellSort(int[] arr){
    
    
        int temp = 0;
        int count = 0;
        for (int gap = arr.length/2; gap >0 ; gap/=2) {
    
    
            for (int i = gap; i <arr.length ; i++) {
    
    
                for (int j = i-gap; j >=0 ; j-=gap) {
    
    
                    if (arr[j]>arr[j+gap]){
    
    
                        temp = arr[j];
                        arr[j] = arr[j+gap];
                        arr[j+gap] = temp;
                    }
                }
            }
            System.out.println("希尔排序"+(count++)+"轮"+Arrays.toString(arr));
        }
     }
}      

外层for循环控制的是分多少组,恰巧我们发现这个gap也是我们的步长,步长也是由它来控制,这就很巧了,比如我们第一次分了5个组,就是

for(int gap = 5;gap>0;gap=2){
    
    
    for(int i=gap;i<arr.length;i++){
    
    
        for(int j = i - gap;j>=0;j-=gap){
    
    
            我们就拿第一个数据来举例;
           	83;
            if(8>3){
    
    //a[0]>a[5]
                交换;然后j=j-gap=0-5跳出最内层循环
                    让i++变成6再进入最内层循环
                    也就是这次是a[j=1]和a[j+gap=6]比较	9>5交换
                   	我们的j=j-gap=-4	跳出i变成2再进来依次类推
                    
            }
        }
    }
}

效率较低,我们有插入法的希尔排序,效率更高

移动法希尔排序

为什么交换法的希尔排序会很慢呢?

因为我们的交换temp = arr[j]; arr[j] = arr[j+gap];arr[j+gap] = temp;代价很大的,我们的直接插入排序就直接插入到了那个位置,不需要来回交换,因此交换法希尔排序比较慢

那么移动法希尔排序怎么做呢?

代码

/**
 * @author 王庆华
 * @version 1.0
 * @date 2020/12/24 17:11
 * @Description TODO
 * @pojectname 希尔排序算法
 */
public class ShellSort {
    
    
    public static void main(String[] args) {
    
    
        int[] arr = {
    
    8,9,1,7,2,3,5,4,6,0};
        shellSort2(arr);
        System.out.println(Arrays.toString(arr));
    }
    public static void shellSort2(int[] arr){
    
    
        //增量gap,并逐步缩小增量
        for (int gap = arr.length/2; gap >0 ; gap/=2) {
    
    
            //从第gap个元素,逐个对其所在的组进行直接插入排序
            for (int i = gap; i <arr.length ; i++) {
    
    
                int j = i;//带插入的下标先保存起来
                int temp = arr[j];
                if (arr[j] < arr[j-gap]){
    
    
                    while (j - gap>=0 && temp< arr[j-gap]){
    
    //继续找位置
                        //移动
                        arr[j] = arr[j-gap];
                        j-=gap;//向前移动
                    }
                    //退出循环时,找到位置
                    arr[j] = temp;
                }
            }
        }
    }
}

文字来解释一下

还是老办法拿数据来举例,我们的第一层循环还是用来分组和制定我们的步长的,第一次进去,5组,每组2个数据,步长是5,

第二层循环,我们先获得我们的起始位置,5,temp=arr[5]=3,也就是第六个数据,同样它是第一组的第二个数据哦!!那么进入if判断的时候,我们发现j-gap正好是对应了跟它同组的另一个数据也就是8,他们两个判断,剩下的就跟我们的直接插入排序很像了,不同之处在于我们移动的地方,以前是-1,现在变成了-gap步长,也就是同组见进行了数据交换。还有不懂的我们可以debug在函数的地方打断点,跟着程序走一遍

猜你喜欢

转载自blog.csdn.net/qq_22155255/article/details/111657017