dart实现希尔排序(Shell sort)

2020-11-13  本文已影响0人  锦鲤跃龙

希尔排序(Shell sort)

[toc]

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

1.思路

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

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列进行插入排序


  ///
  /// 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;
  }
上一篇下一篇

猜你喜欢

热点阅读