一、Shell排序的思路
Shell排序实质上是一种优化的插入排序。D.L.Shell在研究后发现,插入排序存在以下规律:
1>在元素基本有序时效率较高,
2>在序列规模较小时效率较高
为了充分利用这个两个规律,Shell排序的思路是:
a>设定步长,每隔一段距离取一个数,将原来需要排序的序列变成几个较小的部分(姑且称之为子串吧),这样就可以满足2>的条件。
b>将每个子串各自排序
c>减小步长,形成新的子串。此时的子串虽然包含更多地元素,但由于经过b>步骤的排序,此时各部分已基本有序,故满足了1>的设定
d>逐次减小步长,最后进行一次步长为1的排序(也即正常的插入排序),但此时整个串已经基本有序,交换次数不会太多,效率会很高。
二、程序实现
在具体实现时步长(Step)的取法并不固定,在数据量较大时不同的Step取法将带来不同的优化效果(根据网上的说法,PosPro未做研究)。
这里仅仅为了说明算法实现的思路,Step取4,2,1,按照从小到大排列。
#20150901 by PosPro
# http://blog.youkuaiyun.com/pospro
def shellSort(alist):
step=4 #初始步长采用4
while step>=1: #step=1时就是最后一次排序
for i in range(step): # i=0,1,...step-1, 恰好是每一个子串起始值所在位置
shellSortCore(alist, i, step)
print 'Num in every %d positon has been sorted'%step #动态显示之用
step=step//2
def shellSortCore(alist, startpos, step):
#这个函数其实就是实现了一个step版的插入算法,
#不熟悉插入算法的话,可以看前一篇博客:http://blog.youkuaiyun.com/pospro/article/details/48091893
n=len(alist) #记录alist的长度
idx=startpos+step #idx恰好是未排序子串中第一个元素的位置
while idx<n:
val=alist[idx] #待插入的值,也即未排序子串中第一个元素的值
newpos=idx #待插入值得位置,初始值为排在有序子串的最后
while alist[newpos-step]>val and newpos>startpos: #若待插入位置不合适,则通过平移,继续寻找
alist[newpos]=alist[newpos-step] #注意这里所有位置的增减都是针对步长值操作的
newpos-=step
alist[newpos]=val #退出循环后,newpos指示的就是val该放置的位置
idx+=step
print alist #仅为动态显示过程之用
三、测试用例和输出
可使用以下代码对算法进行测试:
list1=[9,8,11,3,4,10,6,0,2,1,7,5]
print list1
shellSort(list1)
print list1
输出结果如下,仔细分析一下,就可以体会出Shell算法的处理过程。