Python堆排序介绍与力扣三道堆相关题目分享

简介: Python堆排序介绍与力扣三道堆相关题目分享

堆的定义


堆 是一种特别的二叉树,满足以下条件的二叉树,可以称之为 堆:

完全二叉树;

每一个节点的值都必须 大于等于或者小于等于 其孩子节点的值。

堆 具有以下的特点:

  • 可以在 O(logN)O(logN) 的时间复杂度内向 堆 中插入元素;
  • 可以在 O(logN)O(logN) 的时间复杂度内向 堆 中删除元素;
  • 可以在 O(1)O(1) 的时间复杂度内获取 堆 中的最大值或最小值。


堆的分类


堆 有两种类型:最大堆(大根堆) 和 最小堆(小根堆)。


网络异常,图片无法展示
|

最大堆:堆中每一个节点的值 都大于等于 其孩子节点的值。所以最大堆的特性是 堆顶元素(根节点)是堆中的最大值。

最小堆:堆中每一个节点的值 都小于等于 其孩子节点的值。所以最小堆的特性是 堆顶元素(根节点)是堆中的最小值。


堆的使用场景


用一句话来描述堆的使用场景就是:动态求极值。其中动态和极值两个条件缺一不可。即当我们遇到题目需要对一个数组进行持续的插入、删除,然后最终求top(N)问题时,不用想必然是堆排序问题。


Python堆模块的使用


在Python中,堆模块通过import heapq来导入,这里要说明下Python的堆都是小根堆,那么Python如何来计算大根堆呢?推荐的做法是将所有整数全部转化为负数,那么就实现了小根堆的操作。

heapq有两种方式创建堆

  1. 使用一个空列表,然后使用heapq.heappush()函数把值加入堆中
  2. 使用heap.heapify(list)转换列表成为堆结构

import heapq
# 方法1
nums = [2,5,1,7,9,10,3,4]
heap = []
for num in nums:
    heapq.heappush(heap, num)
while heap:
    print(heapq.heappop(heap))
# 方法2
nums = [2,5,1,7,9,10,3,4]
heapq.heapify(nums)
while heap:
    print(heap.pop())

堆在日常的使用频率上不是很多,如果仅为了刷题,那么只要了解这些内容就足够做题了。当然如果想细致了解堆的构成与实现,等闲下来了专门写一篇文章来详细讲述。


力扣堆题目


堆的题目总体难度在力扣的上算是比较困难的,但是出现和面试时的频率真的很低。53道题目只有3道简单、21道困难、29道中等,今天在这里给大家推荐三道题目,掌握这三道题,这类题型就差不多了...

  1. 1845.座位预约管理系统 是一道简单设计题目,掌握刚才说的动态、极值,那么这道题解起来简直不要太简单
  2. 1046.最后一块石头的重量 是一道考察大根堆的题目,如刚才所说python中我们需要把它转化为小根堆后再进行计算
  3. 313.超级丑数 这道题呢,算是稍难一点的综合题目,需要我们判断堆的重复值

下来分别看看这三道题目吧:


1845.座位预约管理系统


https://leetcode-cn.com/problems/seat-reservation-manager/solution/5731zuo-wei-yu-yue-guan-li-xi-tong-jian-tlmzu/

难度:中等


题目:

请你设计一个管理 n 个座位预约的系统,座位编号从 1 到 n 。

请你实现 SeatManager 类:

  • SeatManager(int n) 初始化一个 SeatManager 对象,它管理从 1 到 n 编号的 n 个座位。所有座位初始都是可预约的。
  • int reserve() 返回可以预约座位的 最小编号 ,此座位变为不可预约。
  • void unreserve(int seatNumber) 将给定编号 seatNumber 对应的座位变成可以预约。

提示:

  • 1 <= n <= 105
  • 1 <= seatNumber <= n
  • 每一次对reserve的调用,题目保证至少存在一个可以预约的座位。
  • 每一次对unreserve的调用,题目保证seatNumber在调用函数前都是被预约状态。
  • 对reserve 和unreserve的调用总共不超过105次。


示例:

示例 1:
输入:
["SeatManager", "reserve", "reserve", "unreserve", "reserve", "reserve", "reserve", "reserve", "unreserve"]
[[5], [], [], [2], [], [], [], [], [5]]
输出:
[null, 1, 2, null, 2, 3, 4, 5, null]
解释:
SeatManager seatManager = new SeatManager(5); // 初始化 SeatManager ,有 5 个座位。
seatManager.reserve();    // 所有座位都可以预约,所以返回最小编号的座位,也就是 1 。
seatManager.reserve();    // 可以预约的座位为 [2,3,4,5] ,返回最小编号的座位,也就是 2 。
seatManager.unreserve(2); // 将座位 2 变为可以预约,现在可预约的座位为 [2,3,4,5] 。
seatManager.reserve();    // 可以预约的座位为 [2,3,4,5] ,返回最小编号的座位,也就是 2 。
seatManager.reserve();    // 可以预约的座位为 [3,4,5] ,返回最小编号的座位,也就是 3 。
seatManager.reserve();    // 可以预约的座位为 [4,5] ,返回最小编号的座位,也就是 4 。
seatManager.reserve();    // 唯一可以预约的是座位 5 ,所以返回 5 。
seatManager.unreserve(5); // 将座位 5 变为可以预约,现在可预约的座位为 [5] 。


分析

类似这种简单类设计题,在日常面试还是比较多的。

这道题我们使用小根堆,解题简直不要太简单。


解题:

import heapq
class SeatManager:
    def __init__(self, n: int):
        self.ret = [i for i in range(1, n + 1)]
    def reserve(self):
        return heapq.heappop(self.ret)
    def unreserve(self, seatNumber):
        heapq.heappush(self.ret, seatNumber)


1046.最后一块石头的重量


https://leetcode-cn.com/problems/last-stone-weight/solution/1046zui-hou-yi-kuai-shi-tou-de-zhong-lia-1xub/

难度:简单

题目:

有一堆石头,每块石头的重量都是正整数。

每一回合,从中选出两块 最重的 石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:

如果 x == y,那么两块石头都会被完全粉碎;

如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。

最后,最多只会剩下一块石头。返回此石头的重量。如果没有石头剩下,就返回 0。

  • 1 <= stones.length <= 30
  • 1 <= stones[i] <= 1000


示例:

输入:[2,7,4,1,8,1]
输出:1
解释:
先选出 7 和 8,得到 1,所以数组转换为 [2,4,1,1,1],
再选出 2 和 4,得到 2,所以数组转换为 [2,1,1,1],
接着是 2 和 1,得到 1,所以数组转换为 [1,1,1],
最后选出 1 和 1,得到 0,最终数组转换为 [1],这就是最后剩下那块石头的重量。


分析

由于Python不支持大根堆,所以我们需要在预处理的时候,将所有数据转为负数用于适配小根堆。

循环判断的条件当然是堆内数据大于1,当为0和1时表示获取到结果,返回即可。

循环过程中,每次pop出堆内最小的两个数后,对两数根据题意进行比较:

  • 若两数相等,都碾碎
  • 若两数不相等,则将差值重新加入堆中

重复上面流程,最终即可获取结果。


解题:

import heapq
class Solution:
    def lastStoneWeight(self, stones):
        stones = [-i for i in stones]
        heapq.heapify(stones)
        while len(stones) > 1:
            one = heapq.heappop(stones)
            two = heapq.heappop(stones)
            if one != two:
                heapq.heappush(stones, one - two)
        return -stones[0] if stones else 0


313.超级丑数


https://leetcode-cn.com/problems/super-ugly-number/solution/313chao-ji-chou-shu-dui-pai-xu-si-lu-jia-v4iv/

难度:中等


题目:

编写一段程序来查找第 n 个超级丑数。

超级丑数是指其所有质因数都是长度为 k 的质数列表 primes 中的正整数。

说明:

  • 1 是任何给定 primes 的超级丑数。
  • 给定 primes 中的数字以升序排列。
  • 0 < k ≤ 100, 0 < n ≤ 106, 0 < primes[i] < 1000 。
  • 第 n 个超级丑数确保在 32 位有符整数范围内。


示例:

输入: n = 12, primes = [2,7,13,19]
输出: 32 
解释: 给定长度为 4 的质数列表 primes = [2,7,13,19],
    前 12 个超级丑数序列为:[1,2,4,7,8,13,14,16,19,26,28,32] 。


分析

我们可以动态维护一个当前最小的超级丑数。找到第一个,我们将其移除,再找下一个当前最小的超级丑数。

这样经过 n 轮,我们就得到了第 n 小的超级丑数。这种动态求极值的方式,符合堆排序的操作条件。

  1. 初始化ret = 1 为默认的返回值
  2. 我们通过for循环的方式每次找到一个最小值,默认为1。
  3. 最小值tmp出堆时,分别和primes中的每次元素p相乘后入堆。
  4. 此时,我们将tmp赋值给ret
  5. 如此反复3、4操作,直到取到第 n 个超级丑数。

在3 操作的时候,我们需要注意,由于在计算时可能存在相同值的场景,所以在出堆后,需要判断当前堆的最小值是否等于tmp,

如果等于,则需要持续出堆,一直到不相等为止。


解题:

import heapq
class Solution:
    def nthSuperUglyNumber(self, n, primes):
        hq = [1]
        ret = 1
        for i in range(n):
            tmp = heapq.heappop(hq)
            while hq and hq[0] == tmp:
                heapq.heappop(hq)
            for p in primes:
                heapq.heappush(hq, p * tmp)
            ret = tmp
        return ret




相关文章
|
1月前
|
算法 Python
用 Python 实现堆排序。
用 Python 实现堆排序。
13 3
|
3月前
|
算法 程序员 调度
python堆(Heapq)
python堆(Heapq)
39 3
|
4月前
|
算法 Python
python堆-完全二叉树--完全解读
python堆-完全二叉树--完全解读
52 0
|
2月前
|
机器学习/深度学习 算法 数据挖掘
python高级在线题目训练-第二套·主观题
python高级在线题目训练-第二套·主观题
39 0
|
17天前
|
安全 数据安全/隐私保护 Python
292: 程序设计C 实验五 题目三 设计密码(python)
292: 程序设计C 实验五 题目三 设计密码(python)
|
2月前
|
数据采集 大数据 Python
学Python静不下来,看了一堆资料还是很迷茫是为什么
学Python静不下来,看了一堆资料还是很迷茫是为什么
30 2
学Python静不下来,看了一堆资料还是很迷茫是为什么
|
2月前
|
机器学习/深度学习 数据采集 算法
python高级在线题目训练-第二套
python高级在线题目训练-第二套
91 0
|
2月前
|
机器学习/深度学习 存储 算法
python高级在线题目训练-第一套-主观题
python高级在线题目训练-第一套-主观题
35 0
|
2月前
|
机器学习/深度学习 自然语言处理 算法
python高级在线题目训练-第一套
python高级在线题目训练-第一套
98 0
|
3月前
|
算法 Python Java
Python每日一练(20230429) 地下城游戏、杨辉三角II、旋转数组
Python每日一练(20230429) 地下城游戏、杨辉三角II、旋转数组
36 0
Python每日一练(20230429) 地下城游戏、杨辉三角II、旋转数组

热门文章

最新文章