您好,欢迎来到易妖游戏网。
搜索
您的当前位置:首页NB排序算法 - 堆排序

NB排序算法 - 堆排序

来源:易妖游戏网

NB排序算法 - 堆排序

Created: March 9, 2022 8:11 PM
Introduction: 介绍堆排序
Source: 原创
Tags: 算法专栏


堆排序 - 什么是堆

堆:一种特殊的完全二叉树结构,其分为以下两种:

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

如图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5RpI10fZ-174156832)(NB%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%20-%20%E5%A0%86%202d06c/Untitled.png)]

堆的向下调整

假设:节点的左右子树都是堆,但自身不是堆,即下图这种情况:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8Qm4QMay-174156838)(NB%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%20-%20%E5%A0%86%202d06c/Untitled%201.png)]

请问这个是堆吗? 不是,原因就在于不满足大小堆的条件,其根节点比其孩子节点小,其余节点皆比其孩子节点大

此时就可以通过向下调整来将其变换成一个堆:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1pD7dxr0-17415683901)(NB%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%20-%20%E5%A0%86%202d06c/Untitled%202.png)]

堆排序的过程

向下调整方法:

def sift(li,low,high):
    """
    :param li: 列表
    :param low: 堆的根节点位置
    :param high: 堆的最后一个元素的位置
    :return:
    """
    i = low             # i最开始指向根节点
    j = 2 * i + 1       # j开始是左孩子
    tmp = li[low]       # 把堆顶存起来
    while j <= high:    # 只要j位置有数
        if j + 1 <= high and li[j+1] > li[j]: # 如果右孩子有,比较左右孩子,如果右孩子比较大
            j += 1      # j指向右孩子
        if li[j] > tmp:
            li[i] = li[j]
            i = j       # 往下看一层
            j = 2 * i + 1
        else:           # tmp更大,把tmp放到i的位置上
            li[i] = tmp # 把tmp放到合适的位置
            break
    else:
        li[i] = tmp     # 当j位置比high大时,把tmp放在叶子节点上

堆排序方法:

def heap_sort(li):
    # 1、构造堆
    n = len(li)
    for i in range((n-1-1) // 2,-1,-1):
        # i表示建堆的时候调整的部分的根的下标
        sift(li,i,n)
    # 建堆完成了

    # 挨个出数
    for i in range(n-1,-1,-1):
        # i指向当前堆的最后一次元素
        li[0],li[i] = li[i],li[0]
        sift(li,0,i-1)  # i-1是新的high

堆排序的效率

  • 向下调整过程时间复杂度: O ( l o g n ) O(logn) O(logn)
  • 整个堆排序时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)

Python内置堆排序模块

python内置好了堆排序模块,要使用只需导入heapq模块即可

import heapq

如果利用其进行堆排序呢?

# 建堆(默认建小根堆)
heapq.heapify(li)

# 弹出堆元素
for i in range(len(li)):
	print(heapq.heappop(li),end=',')

堆排序 - topk问题

解决思路:

  • 排序后切片: O ( n l o g n ) O(nlogn) O(nlogn)
  • 排序LowB三人组: O ( k n ) O(kn) O(kn)

如果只需要求前k大的数,其实用LowB排序更好,排序k趟,不用像堆排序一样先全部排好序再取前k大的数。

更好的方法:堆排序思路 O ( n l o g k ) O(nlogk) O(nlogk)

  1. 取列表前k个元素建立一个小根堆。堆顶就是目前第k大的数。
  2. 依次向后遍历原列表,对于列表中的元素,如果小于堆顶,则忽略该元素;如果大于堆顶,则将堆顶更换为该元素,并且对堆进行一次调整;
  3. 遍历列表所有元素后,倒序弹出堆顶。
def topk(li,k):
    # 1、建堆
    heap = li[0:k]
    for i in range((k-2)//2,-1,-1):
        sift(heap,i,k-1)
    # 2、遍历
    for i in range(k,len(li)):
        if li[i] > heap[0]:
            heap[0] = li[i]
            sift(heap,0,k-1)
    # 3、重新调整出数
    for i in range(k-1,-1,-1):
        heap[0],heap[i] = heap[i],heap[0]
        sift(heap,0,i-1)
    return heap

相关参考

清华大学博士讲解Python数据结构与算法:

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- vipyiyao.com 版权所有 湘ICP备2023022495号-8

违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务