希尔排序

1959 年一个叫Donald L. Shell (March 1, 1924 – November 2, 2015)的美国人在Communications of the ACM 国际计算机学会月刊发布了一个排序算法,从此名为希尔排序的算法诞生了。

注:ACM = Association for Computing Machinery,国际计算机学会,世界性的计算机从业员专业组织,创立于1947年,是世界上第一个科学性及教育性计算机学会。

希尔排序是直接插入排序的改进版本。因为直接插入排序对那些几乎已经排好序的数列来说,排序效率极高,达到了O(n)的线性复杂度,但是每次只能将数据移动一位。希尔排序创造性的可以将数据移动n位,然后将n一直缩小,缩到与直接插入排序一样为1,请看下列分析。

希尔排序属于插入类排序算法。

一、算法介绍

有一个N个数的数列:

  1. 先取一个小于N的整数d1,将位置是d1整数倍的数们分成一组,对这些数进行直接插入排序。
  2. 接着取一个小于d1的整数d2,将位置是d2整数倍的数们分成一组,对这些数进行直接插入排序。
  3. 接着取一个小于d2的整数d3,将位置是d3整数倍的数们分成一组,对这些数进行直接插入排序。
  4. ...
  5. 直到取到的整数d=1,接着使用直接插入排序。

这是一种分组插入方法,最后一次迭代就相当于是直接插入排序,其他迭代相当于每次移动n个距离的直接插入排序,这些整数是两个数之间的距离,我们称它们为增量。

我们取数列长度的一半为增量,以后每次减半,直到增量为1。

举个简单例子,希尔排序一个 12 个元素的数列:[5 9 1 6 8 14 6 49 25 4 6 3],增量d的取值依次为:6,3,1

x 表示不需要排序的数

取 d = 6 对 [5 x x x x x 6 x x x x x] 进行直接插入排序,没有变化。
取 d = 3 对 [5 x x 6 x x 6 x x 4 x x] 进行直接插入排序,排完序后:[4 x x 5 x x 6 x x 6 x x]。
取 d = 1 对 [4 9 1 5 8 14 6 49 25 6 6 3] 进行直接插入排序,因为 d=1 完全就是直接插入排序了。

越有序的数列,直接插入排序的效率越高,希尔排序通过分组使用直接插入排序,因为步长比1大,在一开始可以很快将无序的数列变得不那么无序,比较和交换的次数也减少,直到最后使用步长为1的直接插入排序,数列已经是相对有序了,所以时间复杂度会稍好一点。

在最好情况下,也就是数列是有序时,希尔排序需要进行logn次增量的直接插入排序,因为每次直接插入排序最佳时间复杂度都为:O(n),因此希尔排序的最佳时间复杂度为:O(nlogn)

在最坏情况下,每一次迭代都是最坏的,假设增量序列为:d8 d7 d6 ... d3 d2 1,那么每一轮直接插入排序的元素数量为:n/d8 n/d7 n/d6 .... n/d3 n/d2 n,那么时间复杂度按照直接插入的最坏复杂度来计算为:

假设增量序列为 ⌊N/2⌋ ,每次增量取值为比上一次的一半小的最大整数。

O( (n/d8)^2 + (n/d7)^2 + (n/d6)^2 + ... + (n/d2)^2 + n^2)

= O(1/d8^2 + 1/d7^2 + 1/d6^2 + ... + 1/d2^2 + 1) * O(n^2)
= O(等比为1/2的数列和) * O(n^2)
= O(等比求和公式) * O(n^2)
= O( (1-(1/2)^n)/(1-1/2) ) * O(n^2)
= O( (1-(1/2)^n)*2 ) * O(n^2)
= O( 2-2*(1/2)^n ) * O(n^2)
= O( < 2 ) * O(n^2)

所以,希尔排序最坏时间复杂度为O(n^2)

不同的分组增量序列,有不同的时间复杂度,但是没有人能够证明哪个序列是最好的。Hibbard增量序列:1,3,7,···,2n−1是被证明可广泛应用的分组序列,时间复杂度为:Θ(n^1.5)

希尔排序的时间复杂度大约在这个范围:O(n^1.3)~O(n^2),具体还无法用数学来严格证明它。

希尔排序不是稳定的,因为每一轮分组,都使用了直接插入排序,但分组会跨越n个位置,导致两个相同的数,发现不了对方而产生了顺序变化。

二、算法实现

package main

import "fmt"

// 增量序列折半的希尔排序
func ShellSort(list []int) {
// 数组长度
n := len(list) // 每次减半,直到步长为 1
for step := n / 2; step >= 1; step /= 2 {
// 开始插入排序,每一轮的步长为 step
for i := step; i < n; i += step {
for j := i - step; j >= 0; j -= step {
// 满足插入那么交换元素
if list[j+step] < list[j] {
list[j], list[j+step] = list[j+step], list[j]
continue
}
break
}
}
}
} func main() {
list := []int{5}
ShellSort(list)
fmt.Println(list) list1 := []int{5, 9}
ShellSort(list1)
fmt.Println(list1) list2 := []int{5, 9, 1, 6, 8, 14, 6, 49, 25, 4, 6, 3}
ShellSort(list2)
fmt.Println(list2) list3 := []int{5, 9, 1, 6, 8, 14, 6, 49, 25, 4, 6, 3, 2, 4, 23, 467, 85, 23, 567, 335, 677, 33, 56, 2, 5, 33, 6, 8, 3}
ShellSort(list3)
fmt.Println(list3)
}

输出:

[5]
[5 9]
[1 3 4 5 6 6 6 8 9 14 25 49]
[1 2 2 3 3 4 4 5 5 6 6 6 6 8 8 9 14 23 23 25 33 33 49 56 85 335 467 567 677]

按照之前分析的几种排序算法,一般建议待排序数组为小规模情况下使用直接插入排序,在规模中等的情况下可以使用希尔排序,但在大规模还是要使用快速排序,归并排序或堆排序。

系列文章入口

我是陈星星,欢迎阅读我亲自写的 数据结构和算法(Golang实现),文章首发于 阅读更友好的GitBook

数据结构和算法(Golang实现)(22)排序算法-希尔排序的更多相关文章

  1. 数据结构和算法(Golang实现)(26)查找算法-哈希表

    哈希表:散列查找 一.线性查找 我们要通过一个键key来查找相应的值value.有一种最简单的方式,就是将键值对存放在链表里,然后遍历链表来查找是否存在key,存在则更新键对应的值,不存在则将键值对链 ...

  2. 数据结构和算法(Golang实现)(27)查找算法-二叉查找树

    二叉查找树 二叉查找树,又叫二叉排序树,二叉搜索树,是一种有特定规则的二叉树,定义如下: 它是一颗二叉树,或者是空树. 左子树所有节点的值都小于它的根节点,右子树所有节点的值都大于它的根节点. 左右子 ...

  3. 数据结构和算法(Golang实现)(28)查找算法-AVL树

    AVL树 二叉查找树的树高度影响了查找的效率,需要尽量减小树的高度,AVL树正是这样的树. 一.AVL树介绍 AVL树是一棵严格自平衡的二叉查找树,1962年,发明者Adelson-Velsky和La ...

  4. 数据结构和算法(Golang实现)(29)查找算法-2-3树和左倾红黑树

    某些教程不区分普通红黑树和左倾红黑树的区别,直接将左倾红黑树拿来教学,并且称其为红黑树,因为左倾红黑树与普通的红黑树相比,实现起来较为简单,容易教学.在这里,我们区分开左倾红黑树和普通红黑树. 红黑树 ...

  5. 《Algorithm算法》笔记:元素排序(2)——希尔排序

    <Algorithm算法>笔记:元素排序(2)——希尔排序 Algorithm算法笔记元素排序2希尔排序 希尔排序思想 为什么是插入排序 h的确定方法 希尔排序的特点 代码 有关排序的介绍 ...

  6. 插入排序、冒泡排序、选择排序、希尔排序、高速排序、归并排序、堆排序和LST基数排序——C++实现

    首先是算法实现文件Sort.h.代码例如以下: <pre name="code" class="java">/* * 实现了八个经常使用的排序算法: ...

  7. 学习C#之旅 冒泡排序,选择排序,插入排序,希尔排序[资料收集]

    关于冒泡排序,选择排序,插入排序,希尔排序[资料收集]  以下资料来源与网络 冒泡排序:从后到前(或者从前到后)相邻的两个两两进行比较,不满足要求就位置进行交换,一轮下来选择出一个最小(或最大)的放到 ...

  8. 数据结构和算法(Golang实现)(30)查找算法-2-3-4树和普通红黑树

    文章首发于 阅读更友好的GitBook. 2-3-4树和普通红黑树 某些教程不区分普通红黑树和左倾红黑树的区别,直接将左倾红黑树拿来教学,并且称其为红黑树,因为左倾红黑树与普通的红黑树相比,实现起来较 ...

  9. C语言中的排序算法--冒泡排序,选择排序,希尔排序

    冒泡排序(Bubble Sort,台湾译为:泡沫排序或气泡排序)是一种简单的排序算法.它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来.走访数列的工作是重复地进行直到没 ...

随机推荐

  1. 选择tomcat时候提示Project facet Java version 1.8 is not supported.解决办法

    是因为jdk版本不一致导致的,如何解决? 方法一: 选中项目Properties,选择Project Facets,右击选择Java,Change Version 方法二: 在项目的目录下有一个.se ...

  2. 为我开发的API添加华丽的外衣

    在日常开发中,最容易被吐槽的就是代码写的烂,没有注释鬼知道你这个是什么意思啊? 另一个就是文档不齐全,这些接口是干嘛的?参数是什么意思?等等问题. 归根到底还是没有严格的开发规范,最重要的还是要有方便 ...

  3. 如何将自己的测试脚本分离成PO模式的测试框架

    1 PO模式 1.1 PO模式介绍 Page Object Model 测试页面和测试脚本分离,即页面封装成类,供测试脚本调用. (将项目分为page.py和test.py) 测试用例:就是excel ...

  4. 一明单词本持续更新ing...

    introductionshuffingdeployspecifyingreliableclusters programming scalemachinesdeliveringsubmarineadd ...

  5. 扫描工具-Arachni

    ./arachni_console          #进入命令行模式 ./arachni_web               #启用web服务,进入web操作模式 [属于web 应用] http:/ ...

  6. Servlet读取前端的request payload

    这几天遇见了一个很头疼的事,当我想用表单上传文件时,后端servlet读取不到文件的信息 网上搜索,说是要将form添加这个属性enctype="multipart/form-data&qu ...

  7. Lack of free swap space on Zabbix server

    在模板(Template)里找到Linux OS模板,修改触发器 配置>模板>Template OS Linux>触发器 找到swap关键字 修改 {Template OS Linu ...

  8. MySQL常用存储引擎:MyISAM与InnoDB之华山论剑

    一 MyISAM 1.1 MyISAM简介 MyISAM是MySQL的默认数据库引擎(5.5版之前),由早期的 ISAM (Indexed Sequential Access Method:有索引的顺 ...

  9. python中的可变和不可变对象 有序和无序对象

    可变对象和不可变对象的定义:对象存放在地址的值是否可以被改变 不可变对象包括:整形int.浮点型float .字符串str .元祖tuple.布尔型boole 可变对象包括 :列表list.集合set ...

  10. 基于STM32F030F4P9和STM32 CUBEMX 输出PWM波形

    STM32F030F4P9定时器功能比较丰富,在此记录项目中使用其自动输出PWM波形(频率:50HZ).CubeMX配置定时器如下图说明. 在此定时器基础时钟为48MHZ,配置中不做分频处理,预分频系 ...