排序-希尔排序
时间线
- 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(n∗logn)
实现
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('直接插入排序','希尔排序'))
如上