python排序算法 之 快排、堆排序、归并排序、希尔排序

快速排序

    快排思路:

        1.取一个元素m(第一个元素),使元素p归位

        2.列表被p分成两部分,左边都比p小,右边都比p大

        3.递归完成排序

    算法关键点:

        1.整理

        2.递归

        总共分为logn层,每层一次partition,复杂度为:O(nlogn)

#  添加装饰器 查看排序所需要的时间
def cal_time(func):
    def wrapper(*args,**kwargs):
        t1 = time.time()
        result = func(*args, **kwargs)
        t2 = time.time()
        print("%s running time: %s secs." %(func.__name__,t2 - t1))
        return result
    return wrapper
# 分割序列的函数


def partition(li,left,right):
    item = li[left]
    while left < right:
        while left < right and li[right] > item:
            right -= 1
        li[left] = li[right]
        while left < right and li[left] < item:
            left += 1
        li[right] = li[left]
    li[left] = item  # left == right
    return left

  partition()函数,右手左手重复动作

# 递归的函数


def quicksort(li,left,right):
    if left < right:  # 递归终止条件
        mid = partition(li,left,right)
        quicksort(li,left,mid-1)
        quicksort(li,mid+1,right)

@cal_time
def quick_sort(li):
    quicksort(li,0,len(li)-1)

      python默认最大递归深度为1000,可以通过sys模块修改最大递归深度

import sys
sys.setrecursionlimit(100000)  # 将递归深度修改为 100000

    快排时间复杂度:最好O(nlogn)  平均O(nlogn)  最坏(n^2)

堆排序

    在学习堆排序之前需要首先学习几个概念:

    二叉树:度不超过2的树(节点最多有两个叉),二叉树的存储方式有链式存储方式和顺序存储方式,本次学习使用顺序存储方式(列表)。

    父节点和左孩子节点的编号下标关系:i  与  2i+1

    父节点与右孩子节点的编号下标关系:i  与  2i+2

    大根堆:一棵完全二叉树,满足任一节点都比其孩子节点大

    小根堆:一棵完全二叉树,满足任一节点都比其孩子节点小

    当根节点的左右子树都是堆时,可以通过一次向下调整来将其变换成一个堆

    堆排序过程:

  1. 建立堆(多次调整,向上调整)
  2. 得到堆顶元素 ,为最大元素
  3. 去掉堆顶元素,将堆最后一个元素放到堆顶,此时可以通过调整(向下调整)重新使堆有序。
  4. 堆顶元素为第二大元素
  5. 重复步骤3,直到堆变空
lwst = [5,3,8,1,2,4]


# 调整函数
def sift(list,low,high):
    i = low
    j = 2 * i + 1
    item = list[i]
    while j <= high:  # 孩子在堆里
        # j < high说明 根节点有右孩子,若< 换成> 调整成小根堆
        if j < high and list[j+1] > list[j]:                                                   
            j = j+1  # j指向右孩子
        if item < list[j]:  # < 换成> 变成小根堆
            list[i] = list[j]
            i = j  # 将调上去的位置作为根节点
            j = 2 * i + 1  # 若有孩子继续调整
        else:
            break
    list[i] = item


def heap_sort(list):
    # 建堆 往上调整堆
    n = len(list)  # 共有n个元素
    # 从最后一个元素往上调整,最后一个节点的父亲编号为n // 2 - 1
    for i in range(n // 2 - 1,-1,-1):                                            
        sift(list,i,n-1)
    # 大根堆已经建好
    # 开始向下调整堆
    for i in range(n-1,-1,-1):
        list[i] ,list[0] = list[0], list[i]  # 节省内存
        sift(list,0,i-1)

    # 向下调整堆的第二种方式
    # li = []
    # for i in range(n-1,-1,-1):
    #     li.append(li[0])
    #     li[0] = list[i]
    #     sift(list,0,i-1)

heap_sort(lwst)
print(lwst)

    堆排序时间复杂度 O(nlogn)

归并排序

    归并思想:

  1. 分解:将列表越分越小,直至分成一个元素
  2. 一个元素是有序的,low == high
  3. 合并:将两个有序列表归并,列表越来越大
# 将两个有序列表进行排序
def merge(list,low,mid,high):
    i = low   # 第一段的开始编号
    j = i + 1  # 第二段的开始编号
    li = []
    while i <= mid and j <= high:
        if list[i] < list[j]:
            li.append(list[i])
            i +=1
        else:
            li.append(list[j])
            j +=1
    while i <= mid:
        li.append(list[i])
        i +=1
    while j <= high:
        li.append(list[j])
        j += 1
    list[low:high+1] = li  # 将li列表写回list中


# 分解与合并函数
def merge_sorts(list,low,high):
    if low < high:  # 递归终止条件
        mid = (low + high) // 2
        merge_sorts(list,low,mid)
        merge_sorts(list,mid+1, high)
        merge(list,low,mid,high)


@cal_time
# 递归函数不能直接加装饰器,耗费资源严重
def merge_sort(list):
    merge_sorts(list,0,len(list) - 1)

    归并排序的时间复杂度  O(nlogn)

    一般情况下:就运行时间而言:快排 < 归并排序 < 堆排序

希尔排序

  1. 希尔排序是一种分组插入排序算法
  2. 首先取一个整数d = n // 2 ,将元素分为d1组,每组相邻元素之间距离为d1(gap),在各组内进行直接插入排序
  3. 取第二个整数 d2 = d1 // 2,重复上述分组排序过程,直到di = 1,即所有元素在同一组内进行直接插入排序
  4. 希尔排序每趟并不使某些元素有序,而是使整体数据越来越接近有序,最后一趟排序使得所有数据有序。
def shell_sort(list):
    n = len(list)
    gap = n // 2  # 第一次指定步长为n // 2
    while gap > 0:  # 控制使gap直到为1,程序结束
        for i in range(gap,n):  # 这个for循环 本质是插入排序的过程
            item = list[i]
            j = i - gap
            while j >= 0 and list[j] > item:
                list[j+gap] = list[j]
                j -= gap
            list[j + gap] = item
        gap //= 2  # 重新指定步长

    希尔排序时间复杂度 O(1.3n) , O(1.3n) < O(nlogn),所以不如上述三种排序。

猜你喜欢

转载自blog.csdn.net/w18306890492/article/details/82560376