希尔排序算法的Python实现(头歌教学实践平台)

第1关:希尔排序的实现

 

任务描述

本关任务:编写代码实现希尔排序。

相关知识

为了完成本关任务,你需要掌握: 1.如何实现希尔排序; 2.希尔排序的算法分析。

希尔排序

对于插入排序,最好的情况是列表已经基本有序,此时比较次数的时间复杂度是O(n)列表越接近有序,插入排序的比较次数就越少。因此,希尔排序以插入排序为基础,将待排序的列表划分为一些子列表,再对每一个子列表执行插入排序,从而实现对插入排序性能的改进。

希尔排序又叫缩小增量排序,划分子列表的特定方法是希尔排序的关键。我们并不是将原始列表直接分成若干个含有连续元素的子列表,而是首先确定一个增量 i 来作为子列表的划分间隔,然后把每间隔为 i 的所有元素选出来组成子列表。在希尔排序的过程中,每一趟排序都将增量不断减小,随着子列表数量的越来越少,无序表整体越来越接近有序,从而能够减少整体排序的比较次数。

图 1 展示的是以 3 为增量的希尔排序。

 图1 以 3 为增量的希尔排序

若对这个含有 9 个数据项的列表以 3 为间隔来划分,则会分成三个子列表:

  • 将列表中下标为 0、3、6 的数据项分成一组,得到子列表 [54,17,44];

  • 将列表中下标为 1、4、7 的数据项分成一组,得到子列表 [26,77,55];

  • 将列表中下标为 2、5、8 的数据项分成一组,得到子列表 [93,31,20]。

然后分别对每一个子列表执行插入排序,得到如图 2 所示的列表。

图2 对每个子列表排序后的结果 

这三次插入排序的过程可描述为:

  • 对于子列表 [54,17,44],首先将 17 与 54 进行比较,17 小于 54,于是将 17 插入到 54 之前。然后将 44 与 54、17 比较,于是将 44 插入到 17 与 54 之间,最终得到有序列表 {17,44,54};

  • 对于子列表 [26,77,55],首先将 77 与 26 进行比较,77 大于 26,于是将 77 插入到 26 之后(不需要移动位置)。然后将 55 与 77、26 比较,于是将 55 插入到 26 与 77 之间,最终得到有序列表 {26,55,77};

  • 对于子列表 [93,31,20],首先将 31 与 93 进行比较,31 小于 93,于是将 31 插入到 93 之前。然后将 20 与 93、31 比较,于是将 20 插入到 31 之前,最终得到有序列表 {20,31,93}。

这就完成了第 1 趟希尔排序,虽然这个列表还没有完全排好序,但经过这一趟对子列表的排序之后,列表中的每个元素更加靠近它最终应该处在的位置。

希尔排序的最后一趟排序一定是将增量减少到 1,图 3 是对图 2 中得到的列表以 1 为增量进行希尔排序,即执行标准的插入排序过程。

 图3 以 1 为增量的排序

通过之前对子列表进行的排序,列表比最开始更加接近有序,此时再进行标准插入排序,能够在一定程度上减少比较和移动的次数。此时,仅需要再进行四次移动就可以完成排序。最后一次插入排序过程中的移动操作有:

  • 将插入项 20 与 26 进行比较,20 小于 26,于是将 26 向右移动一个位置;再将 20 与 17 进行比较,20 大于 17,最终将 20 插入到 17 与 26 之间;

  • 将插入项 31 与 55 进行比较,31 小于 55,于是将 55 向右移动一个位置;再将 31 与 44 进行比较,31 小于 44,于是将 44 向右移动一个位置;再将 31 与 26 进行比较,31 大于 26,最终将 31 插入到 26 与 44 之间;

  • 将插入项 54 与 55 进行比较,54 小于 55,于是将 55 向右移动一个位置;再将 54 与 44 进行比较,54 大于 44,最终将 54 插入到 44 与 55 之间。

对于含有 n 个数据项的列表,希尔排序的增量一般从 n/2 开始,之后的每趟减少到 n/4、n/8……直到 1。图 4 展示了对含有 9 个数据项的列表以 4 为增量划分子列表的一个示例。

图4 以 4 为增量的情况

此外,增量序列中的值不应该有除 1 之外的公因子,否则可能会造成前面某一趟分在同一组已经比较过的数据项,在本趟继续分在同一组,此时这些数据项再次相互比较毫无意义,同时还会增加算法的时间,例如 8、4、2、1 这样的序列就不要选取(8、4、2 有公因子 2)。

希尔排序的算法分析

可能你会觉得希尔排序并不会比插入排序好,因为它最后一步执行了一次完整的插入排序。但事实上,最后的一次排序并不需要很多次的比较和移动,因为已经在之前对子列表的排序中实现了部分排序,这使得最后的排序非常高效。

希尔排序的复杂度分析十分复杂,大致是介于O(n)O(n^{2})之间。使用某些增量值时,它的时间复杂度为O(n^{2})。通过改变增量的大小,比如将增量保持在2k−1(1、3、7、15、31 等),希尔排序的时间复杂度可以达到O(n\tfrac{3}{2})

编程要求

根据提示,在右侧编辑器中的 Begin-End 区间补充代码,根据希尔排序的算法思想完成shellSortgapInsertionSort方法,从而实现对无序表的排序。

测试说明

平台会对你编写的代码进行测试,比对你输出的数值与实际正确的数值,只有所有数据全部计算正确才能通过测试:

测试输入:

  1. 54,26,93,17,77,31,44,55,20

输入说明:输入为需要对其进行排序的无序表。

预期输出:

  1. 增量为 4 : [20, 26, 44, 17, 54, 31, 93, 55, 77]
  2. 增量为 2 : [20, 17, 44, 26, 54, 31, 77, 55, 93]
  3. 增量为 1 : [17, 20, 26, 31, 44, 54, 55, 77, 93]

输出说明:输出的是对无序表进行希尔排序的每一趟排序的结果,以列表的形式展现。其中增量的取值从 n/2 开始,之后的每趟减少到 n/4……直到 1。在本例测试数据中,数据项个数为 9,则增量序列为:4、2、1。

测试输入:

  1. 49,38,65,97,76,13,27

预期输出:

  1. 增量为 3 : [27, 38, 13, 49, 76, 65, 97]
  2. 增量为 1 : [13, 27, 38, 49, 65, 76, 97]

提示:

  1. for i in range(0, 30, 5): # 步长为 5
  2. print(i, end=" ")
  3. print('\n')
  4. for j in range(1, 30, 5):
  5. print(j, end=" ")

输出:

  1. 0 5 10 15 20 25
  2. 1 6 11 16 21 26

开始你的任务吧,祝你成功!

'''请在Begin-End之间补充代码, 完成shellSort和gapInsertionSort函数'''

# 希尔排序
def shellSort(alist):
    sublistcount = len(alist) // 2   # 设定初始增量为n/2
    while sublistcount > 0:  # 不断缩小增量,进行多趟排序
        for startposition in range(sublistcount):  # 每进行一次循环就对某一个子列表进行排序
            # 调用gapInsertionSort函数对子列表进行排序
            # ********** Begin ********** #    
            gapInsertionSort(alist,startposition,sublistcount)
            # ********** End ********** #
        print("增量为",sublistcount,":",alist)
        sublistcount = sublistcount // 2

# 带间隔的插入排序
def gapInsertionSort(alist,start,gap):
    for i in range(start+gap,len(alist),gap):  # 循环的次数表示插入排序的趟数
        currentvalue = alist[i]   # 当前插入项的值
        position = i    # 当前插入项所在的位置
        # 当 position-gap 位置有数据项 且 当前插入项小于 position-gap 位置的数据项,就不断地进行以下操作
        # 将 position-gap 位置的数据项在子列表中向右移动一个位置
        # position 指向 position-gap 位置
        # ********** Begin ********** #    
        while position>=gap and alist[position-gap]>currentvalue:
            alist[position]=alist[position-gap]
            position=position-gap     
        # ********** End ********** #
        alist[position]=currentvalue   # 找到当前插入项的插入位置

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值