十大经典排序算法之希尔排序算法

共 2906字,需浏览 6分钟

 ·

2021-02-16 14:17


点击上方蓝字关注我们

希尔排序

之前我们讲过冒泡排序、选择排序、插入排序,它们的时间复杂度都是 ,比较高,在实际的场景用应用也比较少。

今天我们要讲的希尔排序虽然也是插入排序的一种,但是它是插入排序的一个高效变形,脱离了 的时间复杂度深渊

主要思想

希尔排序的思想简单点说就是有跨度的插入排序,这个跨度会逐渐变小,直到变为 1,变为 1 的时候也就是我们之前讲的简单插入排序了

规范点的描述就是,选择一组递减的整数作为增量序列,最小的增量必须为 1

  • 先用第一个增量把数组分为若干个子数组,每个子数组中的元素下标距离等于增量;
  • 然后对每个子数组进行简单插入排序
  • 再使用第二个增量,然后继续同样的操作,直到增量序列里的增量都使用过一次(增量为 1 时,其实就是对整个数组进行简单插入排序)。

可能你有点疑惑为啥刚开始要进行大跨度的插入排序呢?说实话我刚开始的时候也觉得怪怪的,举个极端的例子帮助你理解下。

假定 ,对于简单的插入排序,如果最小的元素位于最后面的话,那么它就需要和所有的元素比较移动一遍,才可以到达它指定的位置,但是刚开始进行大跨度插入排序的时候,它就可以少比较几次就可以到达前面了

这就是希尔排序的思想。

代码实现

#!/usr/bin/python
# -*- coding: utf-8 -*-
from typing import List
import random



def shell_sort_original(array: List[int]) -> None:
    # 增量序列的初始值
    gap = len(array) // 2
    while gap > 0:
        # 对于 array 进行分组,默认编号是 0, 1, 2, ...
        for i in range(gap):
            # 对于每组进行插入排序
            for j in range(i+gap, len(array), gap):
                temp = array[j]
                index = j - gap
                while index >= 0:
                    if array[index] > temp:
                        array[index+gap] = array[index]
                        index -= gap
                    else:
                        break
                array[index+gap] = temp
        # 增量序列值减小,变为原来的 1/2
        gap //= 2


if __name__ == "__main__":
    min_number, max_number = 0100
    num = 10
    array = [random.randint(min_number, max_number) for _ in range(num)]
    print(array)
    shell_sort_original(array)
    print(array)

乍一看,这个程序一共有四层循环,是不是觉得这个程序怎么可能是插入排序的优化算法呢?但是这个确实是按照希尔排序的思想进行写出来的。

确实,这个程序确实是四层循环,但是呢一个程序的时间复杂度不能单单看循环的层数,更应该看的是程序随着输入的执行次数

希尔排序的写法优化

虽然上面的写法就是完全按照希尔排序的思想来进行实现的,但是呢,写的不够简洁。

下面带你看下一个更常用的写法:

#!/usr/bin/python
# -*- coding: utf-8 -*-
from typing import List
import random


def shell_sort(array: List[int]) -> None:
    # 增量序列
    gap = len(array) // 2
    while gap > 0:
        for i in range(gap, len(array)):
            temp = array[i]
            index = i - gap
            while index >= 0:
                if array[index] > temp:
                    array[index + gap] = array[index]
                    index -= gap
                else:
                    break
            array[index+gap] = temp
        gap //= 2


if __name__ == "__main__":
    min_number, max_number = 0100
    num = 10
    array = [random.randint(min_number, max_number) for _ in range(num)]
    print(array)
    shell_sort(array)
    print(array)

这种实现方法在表面上模糊了对数组分组的概念,而是在进行插入排序的时候通过设置 的变化值为 (之前是 1),来实现的

性能分析

希尔排序的时间复杂度不是我们表面认为的那样,一般来说认为希尔排序的时间复杂度是 ,这个证明起来比较难

空间复杂度的话,希尔排序没有使用额外的空间,进行存储,是原地排序算法

希尔排序算法不是稳定的排序算法。前面我们也提到过,只要涉及到大跨度的排序算法,一般都不是稳定的排序算法。

优化

希尔排序的优化主要是针对增量序列的优化。增量序列如果取得不好,效率比直接插入排序还要低,下面举个例子1:

这个例子就形象说明了这个问题。

于是呢,人们就研究了一些比较好用的增量序列,比如说Hibbard增量序列Sedgewick增量序列

Hibbard增量序列

Hibbard增量序列的取法为

Sedgewick增量序列

Sedgewick增量序列的取法为

总结

本文主要介绍了希尔排序的思想及其代码实现,通过其两种代码实现也可以看到计算机的理论和实现还是不一样的,在理解理论的同时还需要多实践才能更好的学习编程

一个人可以走的更快,一群人可以走得更远

长按扫码关注我们

浏览 21
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报