希尔(谢尔)排序/缩小增量排序(Python实现)

本文深入解析希尔排序算法,探讨其作为直接插入排序改进版的逻辑、时间复杂度及性能优势。通过对比直接插入排序,展示了希尔排序如何通过缩小增量排序使集合趋于有序,从而提升排序效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

排序-希尔排序

时间线

  • 2020年7月4日——完成初稿
  • 2020年7月4日——增加时间性能测试

前言

从学习算法到这篇笔记完成之前,一直都以为希尔排序是多么难懂以及高大上,但其实只要弄懂了就简单了。

以前觉得只要掌握一些常见的即可,但后来才发现,其它的排序算法也有其独特的魅力——不是说这个算法可以实现了什么完成了什么,其魅力在于实现的过程,与其它算法的区别。

定义

一图流

希尔排序是直接插入排序算法的改进,所以本质还是没有改变的,但其处理的方式有所不同

在这里插入图片描述

逻辑

希尔排序又称为缩小增量排序算法,是一种基于插入思想的排序方法。

首先将待排序的集合分成若干个较小的子序列,然后对子序列进行直接插入排序操作。

经过以上调整,整个集合的对象基本有序,最后在对全部记录进行一次直接插入排序。

逻辑思考

可以参考一下 直接插入排序 的笔记

这里粗略讲一下:

还是不讲了,想想扑克排序就行

有空补充

希尔排序也是如此,不过有个增量变量dk:

首先dk为集合长度//2

这个增量变量不断地以2倍的速度变小,

当dk=1时,结束。

即dk=1时,就是最后一次排序,此时的希尔排序是直接插入排序

而dk为其它值时,将进行小规模的直接插入排序。

时间复杂度

希尔排序并不是一款稳定的排序方式,所以其时间复杂度有所相差,但是

最坏情况和平均情况的执行效率相差不是很多
时 间 复 杂 度 : O ( n 1.5 ) 时 间 下 界 : O ( n ∗ l o g n ) 时间复杂度:O(n^{1.5})\\ 时间下界:O(n*logn) O(n1.5)O(nlogn)

实现

Python

# encoding:utf-8
def shell_sort(worklist):
    n = len(worklist)
    dk = n//2

    while dk >= 1:
        for j in range(dk,n):
            i = j
            """ 
                shell排序只会向一个方面进行跳跃,到一端时,
                就会结束
            """
            while i-dk >= 0:
                if worklist[i-dk] > worklist[i]:
                    worklist[i],worklist[i-dk] = worklist[i-dk],worklist[i]
                    i -= dk
                else:
                    break
        dk //=2
    return worklist

思考

  • 希尔排序的时间性能优于直接插入排序

    这句话是希尔排序的特征之一,这是因为希尔排序的最后一次排序前会进行多次缩小增量排序,

    而这些操作会是待处理集合趋于有序,当集合部分有序时,直接插入排序的时间将会大大缩小。

    进行时间性能测试,运行结果如下:
    在这里插入图片描述

    关于算法测试程序如下:

    # encoding:utf-8
    import timeit
    import random
    import matplotlib.pyplot as plt
    class TimeCount(object):
        def __init__(self,func1,func2,repCount,repNum,start,end):
            """ 
                描述:用于测试两个算法的时间性能
                参数:
                    func1,fun2:排序算法1,排序算法2
                    repCount:进行的测试次数
                    repNum:一次测试中执行函数的次数
                    start,end:待排序集合的长度
                    注意:
                        repNum是重复执行函数的次数,没有返回结果
                        repCount是测试的次数,控制返回结果的个数
                        而repnum控制返回结果的数值
                        所以实际上是函数会被执行repcount*repnum次
                    例如:
                        (test,3,100)表示:
                    进行test函数的测试,1次测试中,执行100次test函数,
                    test函数被执行300次,将返回3个结果
            """
            self.func1 = func1
            self.func2 = func2
            self.repCount = repCount
            self.repNum = repNum
            self.start = start
            self.end = end+1
        def getResult(self):
            """
                返回结果:
                        类型:list
                        对象的个数:self.repCount
            """
            result = {}
            result['x'] = [ _ for _ in range(self.start,self.end)]
            result['func1'] = []
            result['func2'] = []
            for i in range(self.start,self.end):
                ls = [random.randint(0,1000) for _ in range(self.start)]
                #func1
                time_func1 = timeit.Timer('{}({})'.format(self.func1.__name__,ls),
                    "from __main__ import {}".format(self.func1.__name__))
                func1_ls = time_func1.repeat(self.repCount,self.repNum)
                func1_avg = sum(func1_ls)/(self.repCount+self.repNum)
                #func1
                time_func2 = timeit.Timer('{}({})'.format(self.func2.__name__,ls),
                    "from __main__ import {}".format(self.func2.__name__))
                func2_ls = time_func2.repeat(self.repCount,self.repNum)
                func2_avg = sum(func2_ls)/(self.repCount*self.repNum)
                result['func1'].append(func1_avg)
                result['func2'].append(func2_avg)
            return result
        def show(self,func1Name,func2Name):
            result = self.getResult()
            x = result['x']
            func1 = result['func1']
            func2 = result['func2']
            plt.scatter(x,func1, label='{}'.format(func1Name), color='y', s=5, marker="+")
            plt.scatter(x,func2, label='{}'.format(func2Name), color='r', s=5, marker="*")
            plt.xlabel('待排序集合的长度')
            plt.ylabel('排序的时间性能')
            plt.title('{}与{}的时间性能的比较'.format(func1Name,func2Name))
            plt.axis([self.start,self.end-1,
                         0,max(max(func1),max(func2))*1.1])
            plt.legend()
            plt.show()
            return True
    
    #直接插入排序和希尔排序的性能测试
    from TimeCount import TimeCount
    from shellSort import shell_sort
    from insertSort import insertion_sort
    #执行30次测试,每次测试重复执行30次函数
    #待排序集合的长度为100到1000
    obj = TimeCount(insertion_sort,shell_sort,30,30,100,1000)
    print(obj.show('直接插入排序','希尔排序'))
    

    如上

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值