【算法Algorithm】希尔(Shell)排序

算法思想

希尔排序是对插入排序的优化。

先把待排序数组进行间隔分组,对每一个分组进行插入排序,相同间隔大小的分组排序完成后,缩小间隔,再分组,再插入排序......直到间隔为1,即最原始的插入排序。

在分隔逐步细化的过程中,大数会集中到后半部分,小数会集中到前半部分,虽然它们此时不一定有序。

代码实现

二分间隔分组法

public static void main(String[] args) {
    // 构造一个无序的数组
    int[] arr = {2, 5, 6, 8, 9, 7, 4, 1, 3, 0, 11, 15, 13, 18, 14, 16, 19, 17, 12, 10};
    sort(arr);
    System.out.println("最终结果:" + Arrays.toString(arr));
}
 
/** 
* 由于希尔排序是插入排序的改进,则先把插入排序的逻辑拿过来,并做修改
*/
public static void insertionSort(int[] arr, int h) {
    /* “新数”的起始位置应该是间隔的位置,其中h是间隔(即第一个数索引位置+h=第二个数索引位置)
       i++:是下一组的第一个“新数”索引位置
    */
    for (int i = h; i < arr.length; i++) {
        /* 内层循环就是已经排好序的数组,j是“新数”的下标
           j -= h:是指“新数”的前一个数(即已排好序的最后一个数),循环则是依次向前比较
           j >= h:交换体中的下标j-h最小就是0(即,h-h),所以应该是j>=h,且防止数组下标越界           
        */
        for (int j = i; j >= h; j -= h) {
            // 拿这个“新数”和已经排好序的数组依次比较,交换,最终找到“新数”应该在的位置
            if (arr[j - h] > arr[j]) {
                int tmp = arr[j - h];
                arr[j - h] = arr[j];
                arr[j] = tmp;
            }
        }
        // 打印每个“新数”插入后的结果
        System.out.println(Arrays.toString(arr));
    }
}


public static void sort(int[] arr) {
    for (int h = arr.length/2; h > 0; h /= 2) {
        insertionSort(arr, h);
    }
}

knuth间隔分组法

public static void sort(int[] arr) {
    int init = 1;
    /* 确定最初的间隔,即最粗的间隔,在for循环中逐步细化;
       init <= arr.length / 3是为了防止第一个间隔大小就比数组长度还大(越界)
    */
    while (init <= arr.length / 3) {
        // knuth间隔分组的公式
        init = 3 * init + 1;
    }    
    // 本例中,最开始的间隔是13(此时,就一个数在那里排,因为13+13就超出数组长度了),然后再依次缩小,直到间隔为1
    for (int h = init; h > 0; h = (h - 1) / 3) {
        insertionSort(arr, h);
    }
}

复杂度和稳定性

时间复杂度(不同的文章可能不一样)

  • 最优时间复杂度:O(n)
  • 最坏时间复杂度:O(n^{2})
  • 平均时间复杂度:O(n^{1.3})

空间复杂度:O(1)

稳定性:稳定

参考文章

https://www.runoob.com/w3cnote/shell-sort.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值