希尔排序也叫作缩小增量排序。

希尔排序的思想就是将一个序列分为若干子序列,在子序列内部进行直接插入排序【间隔非1的】,大幅的向目标位置迈进,而不是一步一步移动,收窄增量,最终为1,最终为1 时,其实就是一次直接插入排序,不过因为序列基本有序,所以这次的直接插入排序是效率最高,整体效率就提升很多,所以说希尔排序是直接插入排序的加强版。

直接插入排序的两个可以改进的点,1、增量为1,每次移动元素步幅小。2、元素无序 的话,需要大量的后移操作。

直接插入在以下情况中效率高

1比较记录较少的时候。

2记录本身是基本有序的,只需要少量 的插入操作。

所谓基本有序就是让大部分比较大的数据在序列后面,大部分比较小的数据在序列前面。

package com.houjun.sort;

/**
 * 希尔排序,直接插入的增强版,直接插入是增量为1的插入排序。希尔排序增量递减
 */
import java.util.Arrays;

public class ShellSort {
    public static void main(String[] args) {
        int[] arr = { 7, 8, 2, 4, 5, 9, 3, 1, 6 };
        shellsort(arr);
        System.out.println(Arrays.toString(arr));
    }

    private static void shellsort(int[] arr) {
        // 希尔排序选出增量
        int dk = arr.length / 3 + 1;
        while (dk != 1) {
            shellInsertSort(arr, dk);
            dk = dk / 3 + 1;
        }
        shellInsertSort(arr, 1);
    }

    /**
     * 增量为dk 的直接插入排序
     *
     * @param arr
     * @param dk
     */
    private static void shellInsertSort(int[] arr, int dk) {
        for (int i = dk; i < arr.length; i++) {// 遍历每个子序列;dk是第一个子序列的开头,dk+1是第二个子序列的开头,dk+n是第n+1个序列的开头
            if (arr[i] < arr[i - dk]) {// 以任意值为增量,则一定存在i-dk的值,我们要判断当前值和他前面 的值
                int j;
                int x = arr[i];// 待插入元素x,arr[i]这个位置会空出来
                arr[i] = arr[i - dk];// 后移arr[i-dk],因为他的值比arr[i]大
                for (j = i - dk; j >= 0 && arr[j] > x; j = j - dk) {// j 必须大于等于0,因为索引j要到子序列的第一个元素
                    // 通过循环,逐次后移比x大的值
//                    arr[j] = arr[j-dk];//第一次进来,j 等于每个子序列的首位,减去增量必为负数,不可这么写
                    arr[j + dk] = arr[j];// 把前面的值后移【后移就是赋给后面的位置】
                }
                arr[j + dk] = x;// 把x赋给待插入的位置
            }
        }
    }
}
02-13 14:28