zoukankan      html  css  js  c++  java
  • 排序算法<No.7>【希尔排序】

    排序算法进入到第7篇,这个也还是比较基础的一种,希尔排序,该排序算法,是依据该算法的发明人donald shell的名字命名的。1959年,shell基于传统的直接插入排序算法,对其性能做了下提升,其思路,主要是基于下面的原因进行的:

    1. 插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率。
    2. 但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位。

    下面,我们就看看希尔排序,具体的实现思路如下:

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

    由上面的分析可知,希尔排序,其实是对原始待排序列的分组插入排序,这种分组,很巧妙,再次体现了分而治之这个思想的厉害。前面介绍过的排序:

    排序算法<No.3>【桶排序】排序算法<No.4>【基数排序】都是分治思想的体现。

    针对上面的实现思路,可以将其分解为如下的代码实现步骤:

    1. 获取原始待排记录的元素个数len,并计算初始增量d=len/2

    2. 通过d = d/2进行循环,对待排记录进行分组,d作为增量值,每间隔d的元素被划分到一组

    3. 对2中每组元素进行直接插入排序算法进行排序

    4. 重复2,3的过程,直到2中计算的增量d=1的这轮排序结束,程序退出,排序完成

    下面,举个栗子来看看,希尔排序的实现过程吧,例如待排序列:59,48,75,107,86,23,37,59,65,14

     

     相应的java代码实现如下:

    /**
     * @author "shihuc"
     * @date   2017年4月9日
     */
    package shellSort;
    
    import java.io.File;
    import java.io.FileNotFoundException;
    import java.util.Scanner;
    
    /**
     * @author "shihuc"
     *
     */
    public class Solution {
    
        /**
         * @param args
         */
        public static void main(String[] args) {
            File file = new File("./src/shellSort/sample.txt");
            Scanner sc = null;
            try {
                sc = new Scanner(file);
                int N = sc.nextInt();
                for (int i = 0; i < N; i++) {
                    int S = sc.nextInt();
                    int A[] = new int[S];
                    for (int j = 0; j < S; j++) {
                        A[j] = sc.nextInt();
                    }
                    shellSort(A);
                    print(A, i, "....");
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } finally {
                if (sc != null) {
                    sc.close();
                }
            }
    
        }
    
        /**
         * 希尔排序的实现过程
         * 
         * @param src 待排序的原始数列
         */
        private static void shellSort(int src[]) {
            
            /*
             * 实现步骤 (1)
             * 获取原始待排记录的元素个数len,并计算初始增量d=len/2
             */
            int d = src.length / 2;
            
            /*
             * 实现步骤(4)
             */
            while (d > 0) {
                
                /*
                 * 实现步骤(3)
                 * 通过增量d对待排序的数列进行分组,进行插入排序. 注意,排序中待比较的元素之间的差距是d
                 */
                for(int i = d; i<src.length; i=i+d){
                    
                    int temp = src[i];
                    int j = i - d;
                    
                    /*
                     * 在获取参考值,也就待排序数列中当前位置的值,然后与其前面的需要排序的组内成员进行
                     * 插入排序。注意,这里的步长是d,和直接插入排序算法中的步长1的差异。认真想想,不难理解的。
                     */
                    while(j >=0 && src[j] > temp){
                        src[j+d] = src[j];
                        j = j - d;
                    }
                    src[j+d] = temp;
                }
                
                /*
                 * 实现步骤(2)
                 * 下面这个过程,其实就是不断迭代计算出新的增量d。
                 */
                d = d / 2;
            }
        }
    
        /**
         * 用来打印输出堆中的数据内容。
         * 
         * @param A
         *            堆对应的数组
         * @param idx
         *            当前是第几组待测试的数据
         * @param info
         *            打印中输出的特殊信息
         */
        private static void print(int A[], int idx, String info) {
            System.out.println(String.format("No. %02d %s ====================== ", idx, info));
            for (int i = 0; i < A.length; i++) {
                System.out.print(A[i] + ", ");
            }
            System.out.println();
        }
    }

    测试栗子文件内容如下:

    6
    10
    59 48 75 107 86 23 37 59 65 14
    7
    2 6 3 4 5 10 9
    10
    2 3 1 4 6 19 11 17 8 16
    11
    12 11 12 17 18 13 19 21 90 23 35
    7
    88 12 22 112 31 11 79
    9
    9 18 21 23 222 121 234 90 211

    测试运行的结果如下:

    No. 00 .... ====================== 
    14, 23, 37, 48, 59, 59, 65, 75, 86, 107, 
    No. 01 .... ====================== 
    2, 3, 4, 5, 6, 9, 10, 
    No. 02 .... ====================== 
    1, 2, 3, 4, 6, 8, 11, 16, 17, 19, 
    No. 03 .... ====================== 
    11, 12, 12, 13, 17, 18, 19, 21, 23, 35, 90, 
    No. 04 .... ====================== 
    11, 12, 22, 31, 79, 88, 112, 
    No. 05 .... ====================== 
    9, 18, 21, 23, 90, 121, 211, 222, 234, 

    其实,还是比较简单的,重点在于分组,插入排序的步长由原来的普通插入排序步长1变成了现在的d。 

  • 相关阅读:
    linux basename 和 dirname 获取当前路径
    灵活的装饰器
    ubuntu 20version install wechat
    git pull 总提示让输入merge 信息
    Linux脚本中$#、$0、$1、$@、$*、$$、$?
    ansible
    MMD讲解
    再生希尔伯特空间与核函数讲解
    流形学习
    聚类
  • 原文地址:https://www.cnblogs.com/shihuc/p/6685062.html
Copyright © 2011-2022 走看看