希尔排序分析及代码实现

6.6 希尔排序

6.6.1 插入排序的问题所在

对于插入排序存在一个问题: 如果待排序列前面的数据已经有序,而恰巧靠后的数字又比较小,后面的数据就要一直比较到最前面,这样需要比较的次数比较多;
希尔排序解决这一问题: 把整个待排序列进行增量分组,对每组使用插入排序算法,没排序完,使得增量减半,随着增量的逐渐减小至1,整个数组被分成一组进行排序,而此时大部分数据已经处于有序位置,
即使无序数据也离正确的位置不远,比较次数少。

6.6.2 希尔排序的思想

插入排序时比较相邻的元素大小,进行插入,希尔排序是在插入排序的基础上,对所有待排序以一个间隔进行插入排序,间隔gap的初始化值可以设置为数组长度的一半,每当一轮以gap为单位的插入排序结束之后,减小gap的值,直至gap等于1,此时最后一轮排序时,整个数组的大部分元素已经处于有序位置,进行最后一轮插入排序。可以理解为希尔排序是在插入排序的基础上进行的。

6.6.3 希尔排序示意图

在这里插入图片描述
在这里插入图片描述

6.6.4 代码分析:
package com.kevin.sortAlgorithm;
import java.util.Arrays;

/**
 * @author : kevin ding
 * @date : 2022/3/3 21:47
 * @description :   对于插入排序存在一个问题: 如果待排序列前面的数据已经有序,而恰巧靠后的数字又比较小,后面的数据就要一直比较到最前面,这样需要比较的次数比较多;希尔排序解决这一问题: 把整个待排序列进行增量分组,对每组使用插入排序算法,没排序完,使得增量减半,随着增量的逐渐减小至1,整个数组被分成一组进行排序,而此时大部分数据已经处于有序位置,即使无序数据也离正确的位置不远,比较次数少
 */
public class ShellSortDemo {
    
    
    public static void main(String[] args) {
    
    
        // int[] array = {8,9,1,7,2,3,5,4,6,0};
        // System.out.println("排序之前,结果为:");
        // System.out.println(Arrays.toString(array));
        // // shellSortAnalysis(array);
        // shellSort(array);

        // 对于  100000个数据进行排序,求其时间
        int[] array = new int[80000];
        for (int i = 0; i < array.length; i++) {
    
    
            array[i] = (int) (Math.random() * 8000000);
        }

        Long startTime = System.currentTimeMillis();
        shellSort(array);
        Long endTime = System.currentTimeMillis();

        System.out.println("希尔排序 所耗费的时间是:" + (endTime-startTime) + "ms");       // 12ms

    }

    public static void shellSortAnalysis(int[] array){
    
    
        // 对于一组数据,首先指定增量初始值为数组长度的一半 gap = 10/2 =5;
        // 下标分组情况为 0,5; 1 6; 2 7; 3 8;  4 9 对这5组数据分别进行插入排序

        // int temp;   // 临时变量用于数据交换
        int insertValue;
        int insertIndex;
        // 第一轮排序:将10个数据分成了5组 需要遍历从gap之后的数 步长为gap
        int gap = 5;
        // 从 array[gap]到array[n-1]的元素为带插入元素,前面的有序元素为分组
        for(int i=gap; i<array.length; i++){
    
    
            //
            insertIndex = i;
            insertValue = array[i];
            // 此时需要进行比较的元素是与insertIndex相差gap的一个元素  比前一个gap的元素小,则需要进行交换
            // 若insertIndex小于gap了,则说明其前面没有相距gap的值了,已经处于了这组中合适的位置
            while(insertIndex >=gap && array[insertIndex - gap] > insertValue){
    
    
                // 需要进行交换
                array[insertIndex] = array[insertIndex- gap];
                array[insertIndex-gap] = insertValue;
                insertIndex -= gap;
            }

        }
        System.out.println("第1 轮shell排序之后:");
        System.out.println(Arrays.toString(array));

        // 第2轮排序:将10个数据分成了5/2 = 2组 需要遍历从gap之后的数 步长为gap
        gap = 2;
        // 从 array[gap]到array[n-1]的元素为带插入元素,前面的有序元素为分组
        for(int i=gap; i<array.length; i++){
    
    
            //
            insertIndex = i;
            insertValue = array[i];
            // 此时需要进行比较的元素是与insertIndex相差gap的一个元素  比前一个gap的元素小,则需要进行交换
            // 若insertIndex小于gap了,则说明其前面没有相距gap的值了,已经处于了这组中合适的位置
            while(insertIndex >=gap && array[insertIndex - gap] > insertValue){
    
    
                // 需要进行交换
                array[insertIndex] = array[insertIndex- gap];
                array[insertIndex-gap] = insertValue;
                insertIndex -= gap;
            }

        }
        System.out.println("第1 轮shell排序之后:");
        System.out.println(Arrays.toString(array));


        // 第3轮排序:将10个数据分成了2/2 = 1组 需要遍历从gap之后的数 步长为gap
        gap = 1;
        // 从 array[gap]到array[n-1]的元素为带插入元素,前面的有序元素为分组
        for(int i=gap; i<array.length; i++){
    
    
            //
            insertIndex = i;
            insertValue = array[i];
            // 此时需要进行比较的元素是与insertIndex相差gap的一个元素  比前一个gap的元素小,则需要进行交换
            // 若insertIndex小于gap了,则说明其前面没有相距gap的值了,已经处于了这组中合适的位置
            while(insertIndex >=gap && array[insertIndex - gap] > insertValue){
    
    
                // 需要进行交换
                array[insertIndex] = array[insertIndex- gap];
                array[insertIndex-gap] = insertValue;
                insertIndex -= gap;
            }

        }
        System.out.println("第1 轮shell排序之后:");
        System.out.println(Arrays.toString(array));
    }

}

6.6.5 代码实现:
public static void shellSort(int[] array){
    
    
        // 上述的逐次过程中,只有gap在发生变化,可以进行循环
        // 定义待比较元素值 和 该元素合适的位置索引
        int insertValue;
        int insertIndex;
        int count = 0;
        // 第一轮排序:将10个数据分成了5组 需要遍历从gap之后的数 步长为gap
        int gap = array.length / 2;
        while(gap > 0){
    
    
            // 从 array[gap]到array[n-1]的元素为带插入元素,前面的有序元素为分组
            for(int i=gap; i<array.length; i++){
    
    
                //
                insertIndex = i;
                insertValue = array[i];
                // 此时需要进行比较的元素是与insertIndex相差gap的一个元素  比前一个gap的元素小,则需要进行交换
                // 若insertIndex小于gap了,则说明其前面没有相距gap的值了,已经处于了这组中合适的位置
                while(insertIndex >=gap && array[insertIndex - gap] > insertValue){
    
    
                    // 需要进行交换
                    array[insertIndex] = array[insertIndex- gap];
                    array[insertIndex-gap] = insertValue;
                    insertIndex -= gap;
                }
            }
            // 一轮循环结束,gap的值减半
            gap /= 2;
        }
    }

猜你喜欢

转载自blog.csdn.net/weixin_43155804/article/details/123265194