排序算法之希尔排序及其增量序列

希尔排序


其他排序方法:选择排序冒泡排序归并排序快速排序插入排序希尔排序堆排序


思想

希尔排序大概就是,选一组递减的整数作为增量序列。最小的增量必须为1:DM>DM−1>...>D1=1" role="presentation" style="position: relative;">DM>DM−1>...>D1=1DM>DM−1>...>D1=1

  • 先用第一个增量把数组分为若干个子数组,每个子数组中的元素下标距离等于增量;
  • 然后对每个子数组进行简单插入排序
  • 再使用第二个增量,继续同样的操作,直到增量序列里的增量都使用过一次。
    (增量为1时,其实就是对整个数组进行简单插入排序)

图解

看图更容易理解吧:
(借用一下慕课的浙大数据结构课件。因为课件原本是ppt,而我只有pdf,所以颜色没有上齐,请将就将就emmm)

性能

希尔排序快不快主要取决于我们怎么取增量序列,原始希尔排序的取法就是:DM=⌊N/2⌋,Dk=⌊Dk+1/2⌋" role="presentation" style="position: relative;">DM=⌊N/2⌋,Dk=⌊Dk+1/2⌋DM=⌊N/2⌋,Dk=⌊Dk+1/2⌋
此增量序列也成为Shell增量序列
原始希尔排序最坏的时间复杂度为O(n2)" role="presentation" style="position: relative;">O(n2)O(n2)

代码

# 原始希尔排序
# 增量序列为D(M)=N/2, D(k)=D(k+1)/2 向下取整
def shellSort(arr):
    size = len(arr)
    # 正整数右移一位相当于除以2且向下取整
    step = size >> 1
    while step > 0:
        for i in range(step, size):
            j = i
            tmp = arr[j]
            while j >= step:
                if tmp < arr[j - step]:
                    arr[j] = arr[j - step]
                    j -= step
                else:
                    break
            arr[j] = tmp
        step = step >> 1

优化

希尔排序的优化主要是针对增量序列的优化。
增量序列如果取得不好,效率比直接插入排序还要低,下面举个例子(直接借用课件了):

在这个例子里,前几个增量没有起到任何作用(只起到了拖延时间的作用哈哈)。
所以有人就发现了,如果增量之间不互质的话,那有些情况就不管用了。

于是有些大佬们就整出了下面这些增量序列:Hibbard增量序列Knuth增量序列Sedgewick增量序列等等
下面主要介绍Hibbard增量序列Sedgewick增量序列

Hibbard增量序列

Hibbard增量序列的取法为Dk=2k−1" role="presentation" style="position: relative;">Dk=2k−1Dk=2k−1:{1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191...}
最坏时间复杂度为O(N3/2)" role="presentation" style="position: relative;">O(N3/2)O(N3/2);平均时间复杂度约为O(N5/4)" role="presentation" style="position: relative;">O(N5/4)O(N5/4)

先来个Hibbard增量序列的获取代码:

# Hibbard增量序列
# D(i)=2^i−1,i>0
def getHibbardStepArr(n):
    i = 1
    arr = []
    while True:
        tmp = (1 << i) - 1
        if tmp <= n:
            arr.append(tmp)
        else:
            break
        i += 1
    return arr

排序代码稍微修改一下就行:

# 希尔排序(Hibbard增量序列)
def shellSort(arr):
    size = len(arr)
    # 获取Hibbard增量序列
    stepArr = getHibbardStepArr(size)
    # 因为要倒着使用序列里的增量,所以这里用了reversed
    for step in reversed(stepArr):
        for i in range(step, size):
            j = i
            tmp = arr[j]
            while j >= step:
                if tmp < arr[j - step]:
                    arr[j] = arr[j - step]
                    j -= step
                else:
                    break
            arr[j] = tmp

至于为什么要用python内置函数reversed(),而不用其它方法,是因为reversed()返回的是迭代器,占用内存少,效率比较高。
如果先使用stepArr.reverse(),再用range(len(arr))的话,效率会比较低;
而且实测reversed也比range(len(arr) - 1, -1, -1)效率高,故使用reversed();
还有就是先stepArr.sort(reverse=True),再用range(len(arr)),同样效率低。
这几种方法比较的测试代码在这里,有兴趣的朋友可以看看:Python列表倒序输出及其效率

Sedgewick增量序列

Sedgewick增量序列的取法为D=9∗4i−9∗2i+1" role="presentation" style="position: relative;">D=9∗4i−9∗2i+1D=9∗4i−9∗2i+1或4i−3∗2i+1" role="presentation" style="position: relative;">4i−3∗2i+14i−3∗2i+1:{1, 5, 19, 41, 109, 209, 505, 929, 2161...}
最坏时间复杂度为O(N4/3)" role="presentation" style="position: relative;">O(N4/3)O(N4/3);平均时间复杂度约为O(N7/6)" role="presentation" style="position: relative;">O(N7/6)O(N7/6)

Sedgewick增量序列的获取代码:

# Sedgewick增量序列
# D=9*4^i-9*2^i+1 或 4^(i+2)-3*2^(i+2)+1 , i>=0
# 稍微变一下形:D=9*(2^(2i)-2^i)+1 或 2^(2i+4)-3*2^(i+2)+1 , i>=0
def getSedgewickStepArr(n):
    i = 0
    arr = []
    while True:
        tmp = 9 * ((1 << 2 * i) - (1 << i)) + 1
        if tmp <= n:
            arr.append(tmp)
        tmp = (1 << 2 * i + 4) - 3 * (1 << i + 2) + 1
        if tmp <= n:
            arr.append(tmp)
        else:
            break
        i += 1
    return arr

排序代码稍微修改一下就行:

# 希尔排序(Sedgewick增量序列)
def shellSort(arr):
    size = len(arr)
    # 获取Sedgewick增量序列
    stepArr = getSedgewickStepArr(size)
    for step in reversed(stepArr):
        for i in range(step, size):
            j = i
            tmp = arr[j]
            while j >= step:
                if tmp < arr[j - step]:
                    arr[j] = arr[j - step]
                    j -= step
                else:
                    break
            arr[j] = tmp

其他排序方法:选择排序冒泡排序归并排序快速排序插入排序希尔排序堆排序

(0)

相关推荐

  • Python | 深入希尔排序世界

    引言 希尔排序(Shell Sort),是插入排序的一种又称"缩小增量排序",同时它是非稳定排序算法.该方法因 D.L.Shell 于 1959 年提出而得名. 问题描述 希尔排序 ...

  • PHP数据结构-插入类排序:简单插入、希尔排序

    插入类排序:简单插入.希尔排序 总算进入我们的排序相关算法的学习了.相信不管是系统学习过的还是没有系统学习过算法的朋友都会听说过许多非常出名的排序算法,当然,我们今天入门的内容并不是直接先从最常见的那 ...

  • 冒泡排序、插入排序、选择排序、希尔排序

    排序是一个非常经典的问题,它以一定的顺序对一个数组(或一个列表)中的项进行重新排序(可以进行比较,例如整数,浮点数,字符串等)(增加,非递减,递减, 增加,词典等). 有许多不同的排序算法,每个都有其 ...

  • 十种排序算法总结(冒泡、插入、选择、希尔、归并、堆、快速,计数,桶,基数)

    #include<iostream> using  namespace std; void swap1( int *left,  int *right) {      int temp = ...

  • Java排序算法(四)希尔排序2

    希尔排序移步法:分组+直接插入排序组合 一.测试类SortTest import java.util.Arrays; public class SortTest { private static fi ...

  • 图解排序算法(二)之希尔排序

    希尔排序是希尔(Donald Shell)于1959年提出的一种排序算法.希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序,同时该算法是冲破O(n2)的第一 ...

  • 七大排序算法总结

    以下所有动图均来源于一像素博客园 以下代码均使用C 编写 完整代码请到这里下载 稳定排序算法:冒泡排序.插入排序.归并排序 时间复杂度不受数据影响:选择排序.归并排序.堆排序 时间复杂度基本小于n2: ...

  • 图解七大排序算法

    "排序是计算机的核心内容.事实上,从很多方面看,如果没有排序,计算机就不会变成现实." <算法之美:指导工作与生活的算法> 排序算法,或许是我们日常最常见也是使用频率最 ...

  • 十大经典排序算法(动图演示)

    0.算法概述 0.1 算法分类 十种常见排序算法可以分为两大类: 比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此也称为非线性时间比较类排序. 非比较类排序: ...

  • 关于排序算法,看这一篇就够了!这篇看不懂麻烦找我拿红包

    排序算法是<数据结构与算法>中最基本的算法之一. 排序算法可以分为内部排序和外部排序. 内部排序是数据记录在内存中进行排序. 而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排 ...

  • 十大排序算法详解,基本思想+动画演示+C语言实现,太肝了!

    下面的99%的代码都是手动敲出来的,参考了诸多资料,已经经过测试,可以放心食用. 1.冒泡排序 基本思想 冒泡排序基本思想是依次比较两个相邻的元素,如果顺序(如从大到小.首字母从Z到A)错误就把他们交 ...