python算法学习(7)——堆排序


列表排序的重要排序有三种,分别是快速排序、堆排序和归并排序。
这里我们来讨论堆排序:

堆排序

在开始学习这个算法前要对数据结构中的树有所学习,掌握基本的概念可以帮助更好的理解和学习堆排序。
堆是一种特殊的完全二叉树结构,其又分为大根堆和小根堆,(
大根堆:一棵完全二叉树,满足任一节点都比其孩子节点大;
小根堆:一棵完全二叉树,满足任一节点都比其孩子节点小;如下图所示)

在这里插入图片描述

堆排序的向下调整性质

在了解堆之后我们就要想一想怎么完成堆排序呢?
首先,我们要理解一下堆的向下调整性质,即:
①假设根节点的左右子树都是堆,但根节点不满足堆的性质;(如下图)
②可以通过一次向下调整使其变成堆
在这里插入图片描述
而该次向下调整结果则如下所示:
在这里插入图片描述
至此,堆的向下调整性质也就了解其过程了,而向下调整也是堆排序过程中很重要的步骤,其代码如下:

#向下调整函数
def sift(list,low,high):
    '''
    :param li: 列表
    :param low: 堆的根节点
    :param high: 堆的最后一个元素的位置
    :return:
   '''
    i =low            #最开始指向根节点
    j=2*i+1           #开始指向i的左孩子节点
    tmp =list[low]    #把堆顶存起来
    while j<=high:    #j位置有数时一直循环
        if j+1<=high and list[j]<list[j+1]:
            j=j+1     #指向i的右孩子节点
        if list[j]>tmp:
            list[i]=list[j]
            i=j       #接着看下一层
            j=2*i+1
        else:         #tmp大于孩子节点
            list[i]=tmp    #把tmp放在某一级根节点的位置
            break
    else:             #当i在最后一层时,j越界,i已经没有孩子节点
        list[i]=tmp   #把tmp放到i的位置上(叶子结点)

堆排序的具体过程

接下来我们继续来深入学习堆排序的过程(默认排序为大根堆),大致分为以下几步:
①建立一个堆;
②得到堆顶元素为最大元素
③去掉堆顶,将最后一个元素放到堆顶位置,此时可通过一次向下调整使堆再次有序;
④此时堆顶为第二大元素;
⑤不断重复步骤③,直到堆空。

其中去掉堆顶元素,将最后一个元素放到堆顶进行向下调整的过程可以说是堆排序的核心,我们也可以把这个过程叫做“挨个出数”。接下来我们来跟着具体的堆的变化来拆分这个过程:
在这里插入图片描述
我们用上图来表示①②后的结果,即一个大根堆。
在这里插入图片描述
上图是将堆顶元素9去掉;
在这里插入图片描述
接着把最后一个元素3放到堆顶位置;
在这里插入图片描述
进行向下排序,使其再次成为一个大根堆(堆顶元素仍为此时堆的最大元素)
此时就完成了一次出数和堆调整,接着我们只需要不断循环以上过程便可以完成堆排序。

而堆排序的具体代码实现如下:

#堆排序函数
def heap_sort(list):
    n=len(list)
    #开始构建堆
    for i in range((n-2)//2,-1,-1):
        #从最后一个叶子节点的父节点开始调整,i表示一次调整的根节点的下标
        sift(list,i,n-1)
    #构建堆结束
    for high in range(n-1,-1,-1):
        #m一直指向堆的最后一个元素
        list[0],list[high]=list[high],list[0]
        sift(list,0,high-1)      #high-1是一次出数后新的high

li=[i for i in range(20)]
import random
random.shuffle(li)
print(li)
heap_sort(li)
print(li)

运行结果如下:

[17, 5, 6, 10, 18, 4, 12, 8, 9, 16, 19, 2, 15, 1, 0, 3, 11, 7, 14, 13]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]

堆排序和快速排序的时间复杂度是相同的,都为O(nlogn),即使两者的时间复杂度相同,但在具体运行速度上堆排序仍然比快速排序要慢一些。

堆排序的应用——topk问题

topk问题描述
现有n个数,设计算法得到前k大的数(k<n)。
解决方法(思路)
①排序后切片——————————————O(nlogn)
②使用三种简单排序———————————O(mn)
③堆排序(小根堆)———————————O(mlogn)
我们在这里选择使用堆排序的思路来解决topk问题,
其具体代码如下:

##堆排序--topk问题
#向下调整函数(小根堆)
def sift(list,low,high):
    '''
    :param li: 列表
    :param low: 堆的根节点
    :param high: 堆的最后一个元素的位置
    :return:
    '''
    i =low            #最开始指向根节点
    j=2*i+1           #开始指向i的左孩子节点
    tmp =list[low]    #把堆顶存起来
    while j<=high:    #j位置有数时一直循环
        if j+1<=high and list[j]>list[j+1]:
            j=j+1     #指向i的右孩子节点
        if list[j]<tmp:
            list[i]=list[j]
            i=j       #接着看下一层
            j=2*i+1
        else:         #tmp大于孩子节点
            list[i]=tmp    #把tmp放在某一级根节点的位置
            break
    else:             #当i在最后一层时,j越界,i已经没有孩子节点
        list[i]=tmp   #把tmp放到i的位置上(叶子结点)

#topk函数
def topk_sort(list,k):
    heap=list[0:k]
    #1.开始构建小根堆
    for i in range((k-2)//2,-1,-1):
        sift(heap,i,k-1)
    #2.遍历表中所有元素
    for j in range(k,len(list)):
        if list[j]>heap[0]:
            heap[0]=list[j]
            sift(heap,0,k-1)
    #3.倒序依次出数
    for high in range(k-1,-1,-1):
         heap[0],heap[high]=heap[high],heap[0]
         sift(heap,0,high-1)
    return heap

li=[12, 14, 0, 1, 6, 8, 16, 11, 3, 18, 4, 7, 10, 13, 5, 15, 9, 17, 19]
print(li)
print(topk_sort(li,5))

运行结果如下:

[12, 14, 0, 1, 6, 8, 16, 11, 3, 18, 4, 7, 10, 13, 5, 15, 9, 17, 19]
[19, 18, 17, 16, 15]

python中堆排序的内置模块——heapq

大致了解一下其中常用的函数有
heapify(x)———建堆函数
heappush(heap,item)————向堆内元素
heappop(heap)————向堆外出元素

提示:下一篇为重要排序篇——归并排序

  • 38
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

快快飞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值