dart实现希尔排序(Shell sort)

希尔排序(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;
  }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,137评论 6 511
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,824评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,465评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,131评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,140评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,895评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,535评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,435评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,952评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,081评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,210评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,896评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,552评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,089评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,198评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,531评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,209评论 2 357

推荐阅读更多精彩内容