美文网首页
dart实现希尔排序(Shell sort)

dart实现希尔排序(Shell sort)

作者: 锦鲤跃龙 | 来源:发表于2020-11-13 18:04 被阅读0次

    希尔排序(Shell sort)

    [toc]

    1959年由唐纳德·希尔(Donald Shell)提出

    1.思路

    • 希尔排序把序列看作是一个矩阵,分成 𝑚 列,逐列进行排序
      • 𝑚 从某个整数逐渐减为1
      • 当 𝑚 为1时,整个序列将完全有序
    • 因此,希尔排序也被称为递减增量排序(Diminishing Increment Sort)

    矩阵的列数取决于步长序列(step sequence)

    • 比如,如果步长序列为{1,5,19,41,109,...},就代表依次分成109列、41列、19列、5列、1列进行排序
    • 不同的步长序列,执行效率也不同

    1.1举例

    希尔本人给出的步长序列是 𝑛/2^𝑘,比如 𝑛 为16时,步长序列是{1, 2, 4, 8}
    例如
    原数组为
    a1 = [16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1]

    1. 第一次分为8列,则分成2个数组
      [16,15,14,13,12,11,10,9]
      [ 8, 7, 6, 5, 4, 3, 2,1]
      逐列进行比较数组变成
      [ 8, 7, 6, 5, 4, 3, 2,1]
      [16,15,14,13,12,11,10,9]
      然后合并成一个数组
      a2=[ 8, 7, 6, 5, 4, 3, 2,1,16,15,14,13,12,11,10,9]
    2. 再将数组a2分为4列,则分为4个数组
      [ 8, 7, 6, 5]
      [ 4, 3, 2, 1]
      [16,15,14,13]
      [12,11,10, 9]
      逐列进行比较
      [ 4, 3, 2, 1]
      [ 8, 7, 6, 5]
      [12,11,10, 9]
      [16,15,14,13]
      然后合并成一个数组
      a3=[ 4, 3, 2, 1, 8, 7, 6, 5,12,11,10, 9,16,15,14,13]
    3. 再将数组a3分为2列,则分成的那个8个数组
      [ 4, 3]
      [ 2, 1]
      [ 8, 7]
      [ 6, 5]
      [12,11]
      [10, 9]
      [16,15]
      [14,13]
      逐列进行比较
      [ 2, 1]
      [ 4, 3]
      [ 6, 5]
      [ 8, 7]
      [10, 9]
      [12,11]
      [14,13]
      [16,15]
      然后合并成一个数组
      a4=[ 2, 1, 4, 3, 6, 5, 8, 7,10, 9,12,11,14,13,16,15]
    4. 在将数组a4分为1列,则分成16个数组,
      [ 2]
      [ 1]
      [ 4]
      ……
      [16]
      [15]
      逐列进行比较,然后合并成一个数组
      a5=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]完成

    每次进行拆分列数后,逆数对的个数减少
    因此希尔排序底层一般使用插入排序对每一列进行排序,也很多资料认为希尔排序是插入排序的改进版

    1.2代码思路

    1.2.1 编写函数返回步长序列

    这里给希尔给的步长序列希尔本人给出的步长序列是 𝑛/2^𝑘,比如 𝑛 为16时,步长序列是{1, 2, 4, 8}

    
      ///
      /// Author: liyanjun
      /// description:
      ///采用希尔给出的步长序列
      List<int> _createShellStepSequence() {
        List<int> stepSequence = [];
        int step = list.length;
        if (step == 0) return [];
        if (step == 1) return [1];
        while (step > 1) {
           step >>= 1;
          stepSequence.add(step);
        }
        return stepSequence;
      }
    
    

    1.2.2 编写函数对第col列进行插入排序

    • 假设元素在第 col 列、第 row 行,步长(总列数)是 step
    • 那么这个元素在数组中的索引是 col + row * step
    • 所以插入排序for循环中每次增加的数字为step,而不再是1
    • 因此原来插入排序相应的加1减1的操作都变成了 加step减step
    
      ///
      /// Author: liyanjun
      /// description: 分成step列进行排序
      _sout(int step) {
        //col 代表列,从
        for (var col = 0; col < step; col++) {
          //第col列进行排序 进行插入排序 这里的代码是插入排序的移动代码的优化版本
          // 原来相应的加1减1的操作都变成了 加step减step
          for (var begin = col+step; begin < list.length; begin +=step) {
            int cur = begin;
            T v = list[cur];
            while (cur > col && cmpElement(v, list[cur - step]) < 0) {
              list[cur] = list[cur - step];
              cur -= step;
            }
            list[cur] = v;
          }
        }
      }
    

    1.2.3 遍历步长序列

     sort() {
        List<int> stepSequence = _createShellStepSequence();
        for (var step in stepSequence) {
          _sout(step);
        }
      }
    

    2.代码

    /// Date: 2020-11-09 00:01:18
    /// FilePath: /algorithm/sort/shell_sort.dart
    /// Description: 希尔排序
    ///
    
    class ShellSort<T extends Comparable<T>> extends Sort<T> {
      @override
      sort() {
        List<int> stepSequence = _createShellStepSequence();
        for (var step in stepSequence) {
          _sout(step);
        }
      }
    
      ///
      /// Author: liyanjun
      /// description: 分成step列进行排序
      _sout(int step) {
        //col 代表列,从
        for (var col = 0; col < step; col++) {
          //第col列进行排序 进行插入排序 这里的代码是插入排序的移动代码的优化版本
          // 原来相应的加1减1的操作都变成了 加step减step
          for (var begin = col+step; begin < list.length; begin +=step) {
            int cur = begin;
            T v = list[cur];
            while (cur > col && cmpElement(v, list[cur - step]) < 0) {
              list[cur] = list[cur - step];
              cur -= step;
            }
            list[cur] = v;
          }
        }
      }
    
      ///
      /// Author: liyanjun
      /// description:
      ///采用希尔给出的步长序列
      List<int> _createShellStepSequence() {
        List<int> stepSequence = [];
        int step = list.length;
        if (step == 0) return [];
        if (step == 1) return [1];
        while (step > 1) {
          step >>= 1;
          stepSequence.add(step);
        }
        return stepSequence;
      }
    

    验证

    main(List<String> args) {
      // List<int> list = IntergerTools.random(10000, 1, 20000); //生成10000个数,最小是1,最大是20000
      List<int> list = IntergerTools.random(10, 1, 20); //生成10000个数,最小是1,最大是20000
      List<Sort> sortList = [
        // HeapSort<num>(),
        // SelectSort<num>(),
        // BubbleSort2<num>(),
        // BubbleSort1<num>(),
        // BubbleSort<num>(),
        // InsertSort<num>(),
        // InsertSort1<num>(),
        // InsertSort2<num>(),
        // MergeSort<num>(),
        // QuickSort<num>(),
        ShellSort<num>(),
      ];
      testSorts(list, sortList);
    }
    
    void testSorts(List<int> list, List<Sort> sorts) {
      print('排序前 :$list');
      for (Sort sort in sorts) {
        List<int> newList = List.from(list);
        sort.setList(newList);
        sort.sortPrint();
        Asserts.test(IntergerTools.isAscOrder(sort.list));
         print('排序后 :${sort.list}');
      }
      sorts.sort(); //比较
      for (Sort sort in sorts) {
        print(sort);
      }
    }
    
    

    结果

    排序前 :[2, 18, 8, 20, 10, 9, 10, 12, 7, 9]
    排序后 :[2, 7, 8, 9, 9, 10, 10, 12, 18, 20]
    【ShellSort<num>】
    耗时:0.002s (2ms)     比较:27    交换:0
    -----------------
    

    3.时间复杂度

    空间复杂度为O(1),属于不稳定排序
    最好情况是步长序列只有1,且序列几乎有序,时间复杂度为 O(n)
    希尔给出的步长序列 𝑛/2^𝑘,的最坏情况时间复杂度是 O(n^2)

    目前已知的最好的步长序列,最坏情况时间复杂度是 O(n^{4/3}) ,1986年由Robert Sedgewick提出
    步长算法思路:

    1. 如果k是偶数,则步长序列为9*(2^k-2^{k/2})+1
    2. 如果k是奇数,则步长序列为8*2^k-6*2^{(k+1)/2}+1
    
      ///
      /// Author: liyanjun
      /// description: 科学家发现最快的步长序列 ,最坏情况时间复杂度是 $O(n^{4/3})$
      ///
      List<int> _sedgewickStepSequence() {
        List<int> stepSequence = [];
        int k = 0, step = 0;
        while (true) {
          if (k % 2 == 0) {
            int powResult = pow(2, k >> 1); //求2^{k/2}的结果
            step = 1 + 9 * (powResult * powResult - powResult);
          } else {
            int pow1 = pow(2, (k - 1) >> 1); //求2^{k-1/2}的结果
            int pow2 = pow(2, (k + 1) >> 1); //求2^{k-1/2}的结果
            step = 1 + 8 * pow1 * pow2 - 6 * pow2;
          }
          if (step >= list.length) break;
          stepSequence.insert(0, step);
          k++;
        }
        return stepSequence;
      }
    

    相关文章

      网友评论

          本文标题:dart实现希尔排序(Shell sort)

          本文链接:https://www.haomeiwen.com/subject/ilrbbktx.html