排序算法(4):希尔排序

介绍

在很长一段时间内,排序算法都是停留在时间复杂度O(n²)。后来有位科学家名叫希尔,创造出了一种排序方法,突破了O(n²),打破了人们的认知。随后越来越多的优秀算法涌现出来。为了纪念希尔做出的卓越贡献,人们用其名字命名该算法。

希尔排序是对插入排序的优化,甚至可以这么说,插入排序是一种特殊情况下的希尔排序。

算法原理

如果一个数组,它的尾部某个位置上有个很小的数值,根据插入排序算法,它要往前比对很长的队伍才能插入到它的位置。那么希尔就想到,能不能在插入排序之前先尽可能的在一定程度上先做好部分的排序。

他首先引入了一个概念,英文叫做gap。中文翻译有的叫增量,有的叫间距。我觉得间距更贴切些。

就是在数组的首位,隔着一定数量的间距来让这些数值先做一个排序。然后再用一个小间距重复操作。直至gap=1,也就是最后使用插入排序。

这个gap有多种算法,而在希尔原稿中是N/2^k。也就是初始的间距是待排序序列长度的一半,然后每次迭代都将间距减半,直到间距为 1。这个间距序列可以表示为:N/2, N/4, N/8, ..., 1。

举例

给一个乱序的数组:8,5,6,3,9,7,4,2,1。 数组里有9个数字。我看很多希尔排序教学都喜欢用数组长度为12或8这样的偶数。那么数组奇数的gap怎么设置,数组又该怎么划分很少有去说。

首先gap=9/2=4.5。gap为4.5怎么划分呢?我们可以向下取整,那么gap=4。当然你愿意也可以向上取整。这里就向下取整gap=4。

当gap=4时,数组:8,5,6,3,9,7,4,2,1

从第一个数8,往后数4个数字是5,6,3,9。从9开始(这里解释下,与插入排序一样,是从第二个数开始的),那么8,9一组。发现8<9,那么顺序不变。数组为8,5,6,3,9,7,4,2,1

9后面是7那么7往前数4个是5,(5,7),5<7,顺序也不变。数组为8,5,6,3,9,7,4,2,1

7后面是4,4往前数4个是6,(6,4),6>4,顺序变换。数组为8,5,4,3,9,7,6,2,1

因为刚刚4与6换过位置,6后面是2,2往前数4个是3,3<4,顺序不变。数组为8,5,4,3,9,7,6,2,1

2后面是1,1往前数4个是9,9>1,顺序变换。数组为8,5,4,3,1,7,6,2,9

1后面没有数字了,至此当gap为4时的数组是8,5,4,3,1,7,6,2,9

其实就是像一些教学视频一样,先将gap=4找到(8,9,1),(5,7),(6,4),(3,2),(9,1)。再将它们在组内排好从小到大排好,然后再排回到之前对应的位置上。我这边的过程是按照代码的执行过程。

当gap=4/2=2时,数组:8,5,4,3,1,7,6,2,9 8往后数2个,是4.那么从4开始。8>4。交换位置4,5,8,3,1,7,6,2,9(其实这一步我说简单了,先记录4这个值。8>4,就把8这个值赋值给4,数组变成8,5,8,3,1,7,6,2,9,再往前数两个,发现没有值了。那么就把8这个位置的上的值赋值之前记录的4,于是数组变成4,5,8,3,1,7,6,2,9)。

由于4和8交换了位置,所以8后面是3,3往前数2个是5,5>3.交换位置 4,3,8,5,1,7,6,2,9

。。。

大家领会精神就好,我就不一一例举了。实在有点累。

代码

只要你会插入排序算法,然后做一点小改动,就是希尔排序了

js 复制代码
// 插入排序算法
function insetSort(array) {
  const { length } = array;

  for (let i = 1; i < length; i++) {
    let current = array[i];
    let j = i;

    while (j > 0 && array[j - 1] > current) {
      array[j] = array[j - 1];
      j--;
    }
    array[j] = current;
  }

  return array;
}
js 复制代码
 // 希尔排序
 // 插入排序其实就是gap=1的时候。
 
 function shellSort(array) {
  const { length } = array;
  let gap = Math.floor(length / 2);
  // 就多了一个外层循环
  while (gap >= 1) {
  // 观察一下,这里就是插入排序算法,只是1都替换成gap
    for (let i = gap; i < array.length; i++) {
      let current = array[i];
      let j = i;
// gap - 1这里解释一下。插入排序是0,当gap为1,1-1=0。
// 它的含义 因为j是不断减小的,当j的位置在gap-1上往左找gap位置必然超过数组的。
      while (j > gap - 1 && array[j - gap] > current) {
        array[j] = array[j - gap];
        j -= gap;
      }
      array[j] = current;
    }
    // 不要忘记gap也要缩小一半
    gap = Math.floor(gap / 2);
  }

  return array;
}
console.log(insetSort([8, 5, 6, 3, 9, 7, 4, 2, 1])); //[1, 2, 3, 4, 5, 6, 7, 8, 9]

时间复杂度

希尔排序的时间复杂度是依赖于gap的选择。在最坏情况下,希尔排序的时间复杂度为O(n²)。但在平均情况下,希尔排序的时间复杂度为O(n logn)或者更好。希尔排序是一种不稳定的排序算法,通常适用于对大规模数据进行排序。

相关推荐
DS随心转插件5 小时前
Grok生成的html怎么导出——AI导出鸭:大模型结构化输出的“最后一公里”工程化解构
前端·人工智能·ai·html·豆包·deepseek·ai导出鸭
এ慕ོ冬℘゜5 小时前
使用 jQuery 动态渲染表格与状态切换
前端·javascript·jquery
人间凡尔赛6 小时前
2026 前端必修:4 个让 CSS 脱胎换骨的现代新特性(附实战代码)
前端·css
用户2181697049306 小时前
Flutter (二十) 轮播图
前端
前端snow6 小时前
ai agent - RAG 汇总
前端
智购科技自动售货机厂家7 小时前
2026自动售货机AI视觉识别优化:从YOLOv11n模型量化到RKNN部署的端侧推理工程实践~YH
javascript·人工智能·yolo·机器学习·计算机视觉·目标跟踪·perl
计算机魔术师7 小时前
AI为拿高分不惜入侵网站:22个模型作弊审计,真相让人背脊发凉
前端
晓说前端7 小时前
TypeScript 核心语法应用 —— Vue 3 中的使用(下)
前端·javascript·typescript·类型系统
zhangminghuan7 小时前
微信小程序开发核心知识点全景解析(前端必备硬核基础)
前端·微信小程序·小程序
boooooooom7 小时前
尝尝咸淡:从朴素 RAG 到 Graph RAG——一个烹饪问答系统的三层检索升级之路
前端·后端·llm