希尔排序

450 查看

概述

希尔排序的诞生是由于插入排序在处理大规模数组的时候会遇到需要移动太多元素的问题,也称递减增量排序算法。希尔排序的思想是将一个大的数组“分而治之”,划分为若干个小的数组,然后分别对划分出来的数组进行插入排序。

希尔排序是基于插入排序的以下两点性质而提出改进方法的:

  1. 插入排序在对几乎已经排好序的数据操作时, 效率高, 即可以达到线性排序的效率

  2. 但插入排序一般来说是低效的, 因为插入排序每次只能将数据移动一位

效果示意图:

至于如何提高效率的,我们还是看下面的实例吧。

步骤

  1. 取增量,一般取数组长度/2

  2. 按增量取得一个子数列,对子数列按插入排序的方式处理

  3. 将增量递减,重复1,2步骤

  4. 直至增量为为0,数列已经排好序

实例

原始数据:

3 5 2 6 2

5/2=2作为增量,对[3 2 2]进行插入排序,得到

2 5 2 6 3

增量递减2/2=1,对[2 5 2 6 3]进行插入排序,得到

2 2 3 5 6

增量递减为0,排序结束。

代码实现(Java)

package com.coder4j.main.arithmetic.sorting;
    
public class Shell {
    
    /**
     * 希尔排序
     * 
     * @param array
     * @return
     */
    public static int[] sort(int[] array) {
        int step = array.length / 2;
        while (step >= 1) {
            for (int i = step; i < array.length; i++) {
                int temp = array[i];
                int j;
                for (j = i - step; j >= 0 && temp < array[j]; j-=step) {
                    array[j + step] = array[j];
                }
                array[j + step] = temp;
            }
            System.out.println("增量为:" + step + ",结果:");
            for (int k : array) {
                System.out.print(k + " ");
            }
            System.out.println();
            step /= 2;
        }
        return array;
    }

    public static void main(String[] args) {
        int[] array = { 3, 5, 2, 6, 2 };
        int[] sorted = sort(array);
        System.out.println("最终结果");
        for (int i : sorted) {
            System.out.print(i + " ");
        }
    }
}

测试输出结果:

增量为:2,结果:
2 5 2 6 3 
增量为:1,结果:
2 2 3 5 6 
最终结果
2 2 3 5 6 

经测试,与实例中结果一致。

希尔与插入对比

希尔是插入的改进优化,到底能否提高效率呢,我们作一下小测试。

分别对[1 2 3 4][4 3 2 1] 进行两种算法的排序操作,看移动的次数。

比较代码:

package com.coder4j.main.arithmetic.sorting;

public class ShellVSInsert {

    /**
     * 插入排序
     * 
     * @param array
     * @return
     */
    public static int[] insert(int[] array) {
        // 记录移动次数
        int count = 0;
        for (int i = 1; i < array.length; i++) {
            int temp = array[i];
            int j;
            for (j = i - 1; j >= 0 && temp < array[j]; j--) {
                array[j + 1] = array[j];
                count++;
            }
            array[j + 1] = temp;
        }
        System.out.println("插入排序移动次数:" + count);
        for (int k : array) {
            System.out.print(k + " ");
        }
        System.out.println();
        return array;
    }
    
    /**
     * 希尔排序
     * 
     * @param array
     * @return
     */
    public static int[] shell(int[] array) {
        // 记录移动次数
        int count = 0;
        int step = array.length / 2;
        while (step >= 1) {
            for (int i = step; i < array.length; i++) {
                int temp = array[i];
                int j;
                for (j = i - step; j >= 0 && temp < array[j]; j = j - step) {
                    array[j + step] = array[j];
                    count++;
                }
                array[j + step] = temp;
            }
            step /= 2;
        }
        System.out.println("希尔排序移动次数:" + count);
        for (int k : array) {
            System.out.print(k + " ");
        }
        System.out.println();
        return array;
    }
    
    public static void main(String[] args) {
        int[] array1 = { 1, 2, 3, 4 };
        insert(array1);
        int[] array2 = { 1, 2, 3, 4 };
        shell(array2);
        
        int [] array3 = { 4, 3, 2, 1 };
        insert(array3);
        int [] array4 = { 4, 3, 2, 1 };
        shell(array4);
        
    }
    
}

对比了两种极端情况,结果如下:

插入排序移动次数:0
1 2 3 4 
希尔排序移动次数:0
1 2 3 4 
插入排序移动次数:6
1 2 3 4 
希尔排序移动次数:4
1 2 3 4 

可见希尔排序在有些时候可以减少移动次数。