• 数据结构之内部排序--希尔排序


    概要

    -IDE:Pycharm
    -Python版本:python3.x
    -算法分类:内部排序->插入类排序->希尔排序

    算法思想

    希尔排序又称缩小增量排序法,是一种基于插入思想的排序方法。它利用了直接插入排序的最佳性质,首先,将待排序的关键字序列分成若干个较小的序列,对子序列进行直接的插入排序,使整个待排序列排好序。
    过程如下:
    1.首先选定记录间的距离$d_i(i=1)$,在整个待排序记录序列中将所有间隔为$d_1$的记录分成一组,进行组内直接插入排序。
    2.然后选取$i=i+1$,记录间的距离为$d_i(d_i<d_i-_1)$,在整个待排序记录序列中,将所有间隔为$d_i$的记录分成一组,进行组内直接插入排序。
    3.重复步骤2直至记录间的距离$d_i=1$,此时整个只有一个子序列,对该序列进行直接插入排序,完成整个排序过程。

    算法要点

    增量取法

    关于增量$d$的取法,最初希尔提出取$d=[n/2]$,再取$d=[d/2]$,直到$d=1$为止。该思路的缺点是,奇数位置的元素在最后一步才会与偶数位元素进行比较,使得排序效率低。后来Knuth提出$d=[d/3]+1$。此外还有多种取法,但无法证明那种最优。

    逆转数

    为了分析希尔排序的优越性,这里引入逆转数的概念。对于待排序列中的某个记录的关键字,它的逆转数是指在它之前比此关键字大的关键字个数。
    假设:被调换位置的两个关键字之间有$l$个介于两个关键字之间的数。逆转数之和一定会减小$2l+1$
    当逆转数为$0$时则表明整个排序完成。

    稳定性与时间复杂度

    排序算法 稳定性 时间复杂度 最好情况 最坏情况 空间复杂度
    希尔排序 不稳定 $O(n^{1.5})$ $O(1)$

    Python代码清单

    # !/usr/bin/python3
    # _*_  coding:utf-8  _*_
    # 希尔排序
     
    import random, time, sys
     
     
    def SS(number,maxNumber):
        # 生成随机数
        timeStart = time.time()
        listA = [0]  # 0位 一般都空出来,防止out of index 或者做监视哨或者备份某个值。
        for i in range(number):
            listA.append(random.randint(0, maxNumber))  # 添加生成的值。
     
        timeEnd = time.time()
        timeIs = timeEnd - timeStart
        listD = [0, int(number/2)]  # listD用于存放增量d,第0位用0占位,我们从第一位开始循环。
        while True:
            if listD[-1] != 1:  # 判断最后一个元素(增量)是否是1,若不是继续计算增量。
                listD.append(int(listD[-1]/3)+1)  # 计算增量
            else:
                break  # 若是1,则退出循环。
     
        print('生成%d个数的时间是%f' % (number, timeIs))
        # print(listA[1:])  # 从第一位开始输出。0位不显示。
        # print(listD[1:])  # 输出d的增量的列表
        ####################################################
        # 希尔排序算法。  我认为 右边是前,左边是后。
        timeStart = time.time()  # 开始记录时间
        #  算法开始
        for d in listD[1:]:  # 设置循环的次数,与d有关系。
            base = 1 + d  # 从前向后找,从哦能通过d+1位开始。
            for item in range(base, len(listA)):  # 每次增加一位
                if listA[item] < listA[item-d]:  # 如果遇到前面小,后面大的数,
                    listA[0] = listA[item]  # 复制前面的数
                    aim = item-d  # 选定目标的位置。
                    while True:  # 开始循环查询序列。
                        if aim > 0 and listA[0] < listA[aim]:  # 这里应设置条件,以防无限循环。
                            listA[aim + d] = listA[aim]  # 将选定目标位的数后移。
                            aim = aim - d   # 每次向前查找,步长为d
                        else:
                            break  # 条件不符合 break 出
                    listA[aim+d] = listA[0]  # 把目标值放入目标位。加d是因为在循环时多减了d。
        #  算法结束
        timeEnd = time.time()  # 记录结束时间。
        timeIs = timeEnd - timeStart  # 排序花费的时间
        print('排序%d个数,花费的时间是%f' % (number, timeIs))
        # print(listA[1:])  # 打印最后排序的列表。
     
     
    if __name__ == '__main__':
     
        helpInfo = '''
               This program is for Shell's Sort.
               How to use it! Follow the example!
     
               python Shell's_Sort.py 10 100
     
               The 10 representative will generate ten numbers.
               100 representative the max-number you make.
     
           '''
     
        command = sys.argv[0:]  # 获取从键盘输入的值。
        if len(command) != 3 or 'help' in command:  # 判断是否合法,或包含help
            print(helpInfo)  # 打印帮助文本。
        else:
            try:  # 尝试将输入转化为整数。
                number = int(command[1])
                maxNumber = int(command[2])
            except ValueError:  # 数值型错误。
                print(helpInfo)  # 打印帮助。
                sys.exit(1)  # 退出程序。
            SS(number, maxNumber)  # 一切完好,调用函数。
    

    有什么问题请联系我

    QQ:3116316431 (请附上信息)
    E-mail:wongyinlong@yeah.net

  • 相关阅读:
    Java 常见异常种类
    Oracle存储过程记录异常
    oracle定时器
    oracle生成单据号
    oracle计算时间秒数差
    oracle分组后取每组第一条数据
    was部分更新
    数据库分区
    JTA事务管理
    docker
  • 原文地址:https://www.cnblogs.com/Leon-The-Professional/p/9950090.html
Copyright © 2020-2023  润新知