Python heapq 模块:使用堆和优先级队列

2023-12-02

优先队列是鲜为人知但非常有用的数据结构。对于涉及在数据集中查找最佳元素的许多问题,他们提供了易于使用且高效的解决方案。蟒蛇heapq模块是标准库的一部分。它实现了所有低级堆操作以及堆的一些高级常见用途。

A 优先队列是一个功能强大的工具,可以解决各种问题,例如编写电子邮件调度程序、查找地图上的最短路径或合并日志文件。编程充满了最优化问题其中的目标是找到最好的元素。优先级队列和Python中的函数heapq模块通常可以帮助解决这个问题。

在本教程中,您将学习:

  • 什么优先队列以及它们之间的关系
  • 有哪几种问题可以使用堆来解决
  • 如何使用Pythonheapq模块来解决这些问题

本教程适用于熟悉以下内容的 Pythonista:列表, 听写, , 和发电机并正在寻找更复杂的数据结构.

您可以通过从以下链接下载源代码来学习本教程中的示例:

获取源代码: 单击此处获取您将使用的源代码了解本教程中的 Python heapq 模块。

什么是堆?

堆是具体的数据结构,而优先级队列是抽象的数据结构。抽象的数据结构决定了界面,而具体的数据结构定义了实现。

堆通常用于实现优先级队列。它们是用于实现优先级队列抽象数据结构的最流行的具体数据结构。

具体数据结构还指定履约保证。性能保证定义了之间的关系尺寸的结构和时间操作需要。了解这些保证可以让您预测随着输入大小的变化,程序将花费多少时间。

数据结构、堆和优先级队列

抽象数据结构指定操作及其之间的关系。例如,优先级队列抽象数据结构支持三种操作:

  1. 是空的检查队列是否为空。
  2. 添加元素将一个元素添加到队列中。
  3. 弹出元素弹出具有最高优先级的元素。

优先级队列通常用于优化任务执行,其目标是处理具有最高优先级的任务。任务完成后,其优先级会降低,并返回到队列中。

有两种不同的约定来确定元素的优先级:

  1. 最大的元素具有最高优先级。
  2. 最小的元素具有最高优先级。

这两个约定是等效的,因为您始终可以颠倒有效顺序。例如,如果您的元素包含数字,那么使用负数将会颠倒约定。

蟒蛇heapq模块使用第二种约定,通常是两者中更常见的一种。根据该公约,最小的元素具有最高优先级。这听起来可能令人惊讶,但它通常非常有用。在稍后您将看到的现实示例中,此约定将简化您的代码。

笔记:蟒蛇heapq模块和一般的堆数据结构是not旨在允许查找除最小元素之外的任何元素。要按大小检索任何元素,更好的选择是二叉搜索树.

具体数据结构实现抽象数据结构中定义的操作,并进一步指定性能保证。

优先级队列的堆实现保证了推送(添加)和弹出(删除)元素都是对数时间操作。这意味着推入和弹出所需的时间与以 2 为底的对数的元素数量。

对数慢慢成长。以 15 为底的对数约为 4,而以 2 为底的对数约为 40。这意味着,如果算法在 15 个元素上足够快,那么在 1 万亿个元素上它只会慢十倍,并且可能仍然足够快。

在任何关于性能的讨论中,最大的警告是,这些抽象的考虑没有实际测量一个具体的程序并了解该程序的实际意义来得重要。瓶颈是。一般性能保证对于对程序行为进行有用的预测仍然很重要,但这些预测应该得到确认。

堆的实现

堆将优先级队列实现为完全二叉树。在一个二叉树,每个节点最多有两个子节点。在一个完全的二叉树,除了可能最深的一层之外,所有级别始终都是满的。如果最深层不完整,则节点将尽可能位于左侧。

完整性属性表示树的深度是元素数量以 2 为底的对数,向上舍入。这是一个完整二叉树的示例:

Complete Binary Tree Satisfying the Heap Property

在此特定示例中,所有级别均已完成。除了最深的节点之外,每个节点都恰好有两个子节点。三级共七个节点。三是七的以 2 为底的对数,四舍五入。

底层的单个节点称为节点。将树顶部的节点称为根可能看起来很奇怪,但这是编程和计算机科学中的常见约定。

堆中的性能保证取决于元素如何在树上上下渗透。实际结果是堆中的比较次数是树大小的以 2 为底的对数。

笔记:比较有时涉及使用调用用户定义的代码.__lt__()。与堆中执行的其他操作相比,在 Python 中调用用户定义的方法是一个相对较慢的操作,因此这通常会成为瓶颈。

在堆树中,节点中的值始终小于其两个子节点的值。这被称为堆属性。这不同于二叉搜索树,其中只有左节点会小于其父节点的值。

压入和弹出的算法都依赖于暂时违反堆属性,然后通过在单个分支上或下进行比较和替换来修复堆属性。

例如,要将一个元素推送到堆上,Python 将新节点添加到下一个开放槽中。如果底层未满,则将该节点添加到底部的下一个空槽中。否则,将创建一个新级别,然后将元素添加到新的底层。

添加节点后,Python 会将其与其父节点进行比较。如果违反了堆属性,则交换该节点及其父节点,并再次从父节点开始检查。这一直持续到堆属性成立或到达根为止。

类似地,当弹出最小元素时,Python 知道,由于堆属性,该元素位于树的根部。它用最深层的最后一个元素替换该元素,然后检查分支下是否违反了堆属性。

优先级队列的用途

优先级队列以及作为优先级队列实现的堆对于涉及查找某种程度极端的元素的程序非常有用。例如,您可以将优先级队列用于以下任意任务:

  • 从点击数据中获取三个最受欢迎的博客文章
  • 寻找从一个点到另一个点的最快方法
  • 根据到达频率预测哪辆巴士将最先到达车站

您可以使用优先级队列的另一个任务是安排电子邮件。想象一个有多种电子邮件的系统,每种电子邮件都需要以一定的频率发送。一种电子邮件需要每十五分钟发送一次,另一种电子邮件需要每四十分钟发送一次。

调度程序可以将两种类型的电子邮件添加到队列中时间戳指示下次需要发送电子邮件的时间。然后,调度程序可以查看具有最小时间戳的元素(表明它是下一个要发送的元素),并计算发送之前要休眠多长时间。

当调度程序醒来时,它将处理相关电子邮件,将电子邮件从优先级队列中取出,计算下一个时间戳,并将电子邮件放回到队列中的正确位置。

Python 中的堆作为列表heapq模块

尽管您将前面描述的堆视为一棵树,但重要的是要记住它是一棵树完全的二叉树。完整性意味着总是可以知道除了最后一层之外的每一层有多少个元素。因此,堆可以实现为列表。这就是Pythonheapq模块确实如此。

确定索引处元素之间的关系有三个规则k及其周围的元素:

  1. 它的第一个孩子是在2*k + 1.
  2. 它的第二个孩子是在2*k + 2.
  3. 它的父级位于(k - 1) // 2.

笔记://符号是整数除法操作员。它总是向下舍入为整数。

上面的规则告诉您如何将列表可视化为完全二叉树。请记住,元素始终有父元素,但有些元素没有子元素。如果2*k超出列表末尾,则该元素没有任何子元素。如果2*k + 1是一个有效的索引,但是2*k + 2不是,则该元素只有一个子元素。

堆的性质意味着如果h是一个堆,那么下面的永远不会是False:

h[k] <= h[2*k + 1] and h[k] <= h[2*k + 2]

它可能会引发IndexError如果任何索引超出列表的长度,但它永远不会False.

换句话说,元素必须始终小于位于其索引两倍加一和两倍索引加二处的元素。

这是满足堆属性的列表的视觉效果:

Heap Implemented as a List

箭头从元素开始k到元素2*k + 12*k + 2。例如,Python 列表中的第一个元素的索引为0,所以它的两个箭头指向索引12。请注意箭头如何始终从较小的值移动到较大的值。这是检查列表是否满足堆属性的方法。

基本操作

蟒蛇heapq模块在列表上实现堆操作。与许多其他模块不同,它确实not定义一个自定义类。蟒蛇heapq模块具有直接作用于列表的函数。

通常,如上面的电子邮件示例所示,元素将从空堆开始一个接一个地插入到堆中。然而,如果已经有一个需要成为堆的元素列表,那么Pythonheapq模块包括heapify()用于将列表转换为有效的堆。

下面的代码使用heapify()转动a变成一个:

>>>
>>> import heapq
>>> a = [3, 5, 1, 2, 6, 8, 7]
>>> heapq.heapify(a)
>>> a
[1, 2, 3, 5, 6, 8, 7]

你可以检查一下,即使7紧接着8, 列表a仍然遵守堆性质。例如,a[2],即3, 小于a[2*2 + 2],即7.

如你看到的,heapify()就地修改列表但不对其进行排序。不必对堆进行排序即可满足堆属性。然而,由于每个排序列表满足堆属性,运行heapify()在排序列表上不会改变列表中元素的顺序。

Python中的其他基本操作heapq模块假设列表已经是一个堆。值得注意的是,空列表或长度为 1 的列表始终是堆。

由于树的根是第一个元素,因此不需要专用函数来无损读取最小元素。第一个元素,a[0],将永远是最小的元素。

为了在保留堆属性的同时弹出最小元素,Pythonheapq模块定义heappop().

使用方法如下heappop()弹出一个元素:

>>>
>>> import heapq
>>> a = [1, 2, 3, 5, 6, 8, 7]
>>> heapq.heappop(a)
1
>>> a
[2, 5, 3, 7, 6, 8]

该函数返回第一个元素,1,并保留堆属性a。例如,a[1]5a[1*2 + 2]6.

蟒蛇heapq模块还包括heappush()用于将元素推入堆,同时保留堆属性。

以下示例显示将值推送到堆:

>>>
>>> import heapq
>>> a = [2, 5, 3, 7, 6, 8]
>>> heapq.heappush(a, 4)
>>> a
[2, 5, 3, 7, 6, 8, 4]
>>> heapq.heappop(a)
2
>>> heapq.heappop(a)
3
>>> heapq.heappop(a)
4

推后4到堆中,您从中弹出三个元素。自从23已经在堆中并且小于4,它们首先被弹出。

蟒蛇heapq模块还定义了另外两个操作:

  1. heapreplace()相当于heappop()其次是heappush().
  2. heappushpop()相当于heappush()其次是heappop().

这些在某些算法中很有用,因为它们比单独执行这两个操作更有效。

高级别操作

由于优先级队列经常用于合并排序序列,Pythonheapq模块有现成的功能,merge(),用于使用堆来合并多个可迭代对象。merge()假设其输入迭代已经排序并返回迭代器,不是一个列表。

作为使用的示例merge(),这是前面描述的电子邮件调度程序的实现:

import datetime
import heapq

def email(frequency, details):
    current = datetime.datetime.now()
    while True:
        current += frequency
        yield current, details

fast_email = email(datetime.timedelta(minutes=15), "fast email")
slow_email = email(datetime.timedelta(minutes=40), "slow email")

unified = heapq.merge(fast_email, slow_email)

输入到merge()在这个例子中是无限发电机。返回值分配给多变的 unified也是一个无限迭代器。该迭代器将生成要按照未来时间戳的顺序发送的电子邮件。

要调试并确认代码合并正确,您可以打印要发送的前十封电子邮件:

>>>
>>> for _ in range(10):
...    print(next(element))
(datetime.datetime(2020, 4, 12, 21, 27, 20, 305358), 'fast email')
(datetime.datetime(2020, 4, 12, 21, 42, 20, 305358), 'fast email')
(datetime.datetime(2020, 4, 12, 21, 52, 20, 305360), 'slow email')
(datetime.datetime(2020, 4, 12, 21, 57, 20, 305358), 'fast email')
(datetime.datetime(2020, 4, 12, 22, 12, 20, 305358), 'fast email')
(datetime.datetime(2020, 4, 12, 22, 27, 20, 305358), 'fast email')
(datetime.datetime(2020, 4, 12, 22, 32, 20, 305360), 'slow email')
(datetime.datetime(2020, 4, 12, 22, 42, 20, 305358), 'fast email')
(datetime.datetime(2020, 4, 12, 22, 57, 20, 305358), 'fast email')
(datetime.datetime(2020, 4, 12, 23, 12, 20, 305358), 'fast email')

请注意如何fast email安排在每15分钟,slow email安排在每40,并且电子邮件正确交错,以便它们按照时间戳的顺序排列。

merge()不会读取所有输入,而是动态工作。尽管两个输入都是无限迭代器,但打印前十项很快就会完成。

以类似的方式,当用于合并排序序列(如按时间戳排列的日志文件行)时,即使日志很大,这也会占用合理的内存量。

堆可以解决的问题

正如您在上面看到的,堆非常适合增量合并排序序列。您已经考虑过的堆的两个应用是调度定期任务和合并日志文件。然而,还有更多的应用。

堆还可以帮助识别顶部n或底部n事物。蟒蛇heapq模块具有实现此行为的高级函数。

例如,此代码从以下位置获取时间作为输入女子100米决赛2016 年夏季奥运会并打印奖牌获得者或前三名获得者:

>>>
>>> import heapq
>>> results="""\
... Christania Williams      11.80
... Marie-Josee Ta Lou       10.86
... Elaine Thompson          10.71
... Tori Bowie               10.83
... Shelly-Ann Fraser-Pryce  10.86
... English Gardner          10.94
... Michelle-Lee Ahye        10.92
... Dafne Schippers          10.90
... """
>>> top_3 = heapq.nsmallest(
...     3, results.splitlines(), key=lambda x: float(x.split()[-1])
... )
>>> print("\n".join(top_3))
Elaine Thompson          10.71
Tori Bowie               10.83
Marie-Josee Ta Lou       10.86

这段代码使用nsmallest()来自Pythonheapq模块。nsmallest()返回可迭代中的最小元素并接受三个参数:

  1. n指示要返回多少个元素。
  2. iterable标识要比较的元素或数据集。
  3. key是一个可调用函数,用于确定如何比较元素。

在这里,key函数按空格分割行,获取最后一个元素,并将其转换为浮点数。这意味着代码将按运行时间对行进行排序,并返回运行时间最少的三行。这些对应于最快的三名跑步者,从而为您提供金牌、银牌和铜牌获得者。

蟒蛇heapq模块还包括nlargest(),它具有相似的参数并返回最大的元素。如果您想从标枪比赛中获得奖牌,这将很有用,标枪比赛的目标是尽可能远地投掷标枪。

如何发现问题

堆作为优先级队列的实现,是解决涉及极端问题(例如给定度量的最多或最少)的好工具。

还有其他一些词表明堆可能有用:

  • 最大
  • 最小
  • 最大
  • 最小
  • 最好的
  • 最差
  • Top
  • 底部
  • 最大限度
  • 最低限度
  • 最佳的

每当问题陈述表明您正在寻找某些极端元素时,就值得考虑优先级队列是否有用。

有时优先级队列只会部分解决方案的一部分,其余的将是一些变体动态规划。您将在下一节中看到的完整示例就是这种情况。动态编程和优先级队列通常一起使用。

示例:查找路径

以下示例是 Python 的实际用例heapq模块。该示例将使用一种经典算法,作为其一部分,需要一个堆。您可以点击下面的链接下载示例中使用的源代码:

获取源代码: 单击此处获取您将使用的源代码了解本教程中的 Python heapq 模块。

想象一个需要在二维迷宫中导航的机器人。机器人需要从左上角的原点到达右下角的目的地。机器人的内存中有一张迷宫地图,因此它可以在出发前规划出整个路径。

目标是让机器人尽快完成迷宫。

我们的算法是一个变体Dijkstra 算法。在整个算法中保留和更新了三种数据结构:

  1. tentative是从原点到某个位置的暂定路径的地图,pos。该路径称为暂定的因为它是最短的已知的路径,但可能会有所改进。

  2. certain是一组点,其路径tentative地图是肯定成为可能的最短路径。

  3. candidates是一堆有路径的位置。这排序键堆的长度是路径的​​长度。

在每个步骤中,您最多执行四个操作:

  1. 从以下位置弹出一位候选人candidates.

  2. 将候选人添加到certain放。如果候选人已经是该组织的成员certain设置,然后跳过接下来的两个操作。

  3. 找到到达当前候选人的最短已知路径。

  4. 对于每个当前候选者的直接邻居,查看通过该候选者是否给出比当前更短的路径tentative小路。如果是这样,则更新tentative路径和candidates堆上这条新路径。

这些步骤循环运行,直到将目的地添加到certain放。当目的地位于certain设置,你就完成了。算法的输出是tentative到达目的地的路径,即现在certain成为可能的最短路径。

顶层代码

现在您已经了解了该算法,是时候编写代码来实现它了。在实现算法本身之前,编写一些支持代码很有用。

首先,您需要进口蟒蛇heapq模块:

import heapq

您将使用 Python 中的函数heapq模块来维护一个堆,这将帮助您在每次迭代时找到具有最短已知路径的位置。

下一步是将地图定义为代码中的变量:

map = """\
.......X..
.......X..
....XXXX..
..........
..........
"""

地图是一个三引号字符串显示机器人可以移动的区域以及任何障碍物。

虽然更现实的场景是让您从文件中读取地图,但出于教学目的,使用这个简单的地图在代码中定义变量会更容易。该代码适用于任何地图,但在简单地图上更容易理解和调试。

该映射经过优化,易于代码读者理解。点(.)足够轻,看起来很空,但它的优点是可以显示允许区域的尺寸。这X位置标记机器人无法通过的障碍。

支持代码

第一个函数会将地图转换为更易于在代码中解析的内容。parse_map()获取地图并对其进行分析:

def parse_map(map):
    lines = map.splitlines()
    origin = 0, 0
    destination = len(lines[-1]) - 1, len(lines) - 1
    return lines, origin, destination

该函数接受一个映射并返回一个包含三个元素的元组:

  1. 的列表lines
  2. origin
  3. destination

这使得代码的其余部分可以在为计算机设计的数据结构上运行,而不是为人类视觉扫描的能力而设计。

名单lines可以通过索引(x, y)坐标。表达方式lines[y][x]将位置值返回为两个字符之一:

  1. 一个点(".")表示该位置是一个空白区域。
  2. 这封信"X"表示该位置是障碍物。

当您想要查找机器人可以占据哪些位置时,这将很有用。

功能is_valid()计算是否给定(x, y)位置有效:

def is_valid(lines, position):
    x, y = position
    if not (0 <= y < len(lines) and 0 <= x < len(lines[y])):
        return False
    if lines[y][x] == "X":
        return False
    return True

该函数有两个参数:

  1. lines是作为线条列表的地图。
  2. position是作为整数二元组进行检查的位置,指示(x, y)坐标。

为了有效,位置必须位于地图边界内并且没有障碍物。

该函数检查y通过检查长度是否有效lines列表。该函数接下来检查x确保它在内部是有效的lines[y]。最后,现在您知道两个坐标都在地图内,代码通过查看该位置的角色并将该角色与"X".

另一个有用的帮手是get_neighbors(),它找到一个位置的所有邻居:

def get_neighbors(lines, current):
    x, y = current
    for dx in [-1, 0, 1]:
        for dy in [-1, 0, 1]:
            if dx == 0 and dy == 0:
                continue
            position = x + dx, y + dy
            if is_valid(lines, position):
                yield position

该函数返回当前位置周围的所有有效位置。

get_neighbors()小心避免将某个位置识别为自己的邻居,但它确实允许对角邻居。这就是为什么至少有一个dxdy一定不能为零,但两者都非零也可以。

最终的辅助函数是get_shorter_paths(),它找到较短的路径:

def get_shorter_paths(tentative, positions, through):
    path = tentative[through] + [through]
    for position in positions:
        if position in tentative and len(tentative[position]) <= len(path):
            continue
        yield position, path

get_shorter_paths()产生具有以下路径的位置through因为它的最后一步比当前已知的路径短。

get_shorter_paths()有三个参数:

  1. tentative是将位置映射到已知最短路径的字典。
  2. positions是您想要缩短路径的位置的迭代。
  3. through是一个位置,通过该位置,也许有一条更短的路径可以到达positions可以被找寻到。

假设中的所有元素positions可以一步到达through.

功能get_shorter_paths()检查是否使用through因为最后一步将为每个位置提供更好的路径。如果没有已知的到达某个位置的路径,那么任何路径都会更短。如果存在已知路径,则仅在其长度较短的情况下生成新路径。为了使APIget_shorter_paths()更容易使用,一部分yield也是较短的路径。

所有辅助函数都被编写为纯函数,这意味着它们不修改任何数据结构,只返回值。这使得更容易遵循核心算法,该算法完成所有数据结构更新。

核心算法代码

回顾一下,您正在寻找起点和目的地之间的最短路径。

您保留三部分数据:

  1. certain是某些位置的集合。
  2. candidates是候选人堆。
  3. tentative是将节点映射到当前最短已知路径的字典。

一个位置是在certain如果您可以确定最短的已知路径是最短的可能路径。如果目的地是在certain设置,那么到达目的地的最短已知路径无疑是最短可能路径,并且您可以返回该路径。

堆的candidates按已知最短路径的长度组织,并借助 Python 中的函数进行管理heapq模块。

在每一步中,您都会查看具有最短已知路径的候选者。这是堆被弹出的地方heappop()。没有到达该候选者的更短路径——所有其他路径都经过candidates,而且所有这些都更长。因此,当前候选人可以被标记为certain.

然后,您查看所有尚未访问过的邻居,如果遍历当前节点是一种改进,那么您将它们添加到candidates堆使用heappush().

功能find_path()实现这个算法:

 1def find_path(map):
 2    lines, origin, destination = parse_map(map)
 3    tentative = {origin: []}
 4    candidates = [(0, origin)]
 5    certain = set()
 6    while destination not in certain and len(candidates) > 0:
 7        _ignored, current = heapq.heappop(candidates)
 8        if current in certain:
 9            continue
10        certain.add(current)
11        neighbors = set(get_neighbors(lines, current)) - certain
12        shorter = get_shorter_paths(tentative, neighbors, current)
13        for neighbor, path in shorter:
14            tentative[neighbor] = path
15            heapq.heappush(candidates, (len(path), neighbor))
16    if destination in tentative:
17        return tentative[destination] + [destination]
18    else:
19        raise ValueError("no path")

find_path()收到一个map作为字符串,并以位置列表的形式返回从起点到目的地的路径。

这个函数有点长而且复杂,所以让我们一次一点地看一下:

  • 第 2 至 5 行设置循环将查看和更新​​的变量。您已经知道从原点到自身的路径,该路径是长度为 0 的空路径。

  • 6号线定义循环的终止条件。如果没有candidates,则无法缩短路径。如果destination是在certain,那么路径destination不能再短了。

  • 第 7 行至第 10 行让候选人使用heappop(),如果已经存在则跳过循环certain,否则将候选人添加到certain。这确保每个候选者最多被循环处理一次。

  • 第 11 至 15 行使用get_neighbors()get_shorter_paths()找到到相邻位置的更短路径并更新tentative字典和candidates堆。

  • 第 16 至 19 行处理返回正确的结果。如果找到路径,该函数将返回它。虽然计算路径没有最终位置使算法的实现更简单,这是一个更好的 API 来返回它目的地。如果未找到路径,则会引发异常。

将函数分成单独的部分可以让您一次理解它的一部分。

可视化代码

如果该算法实际上由机器人使用,那么机器人可能会通过它应该经过的位置列表表现得更好。然而,为了使结果对人类来说更好看,最好将它们可视化。

show_path()在地图上绘制一条路径:

def show_path(path, map):
    lines = map.splitlines()
    for x, y in path:
        lines[y] = lines[y][:x] + "@" + lines[y][x + 1 :]
    return "\n".join(lines) + "\n"

该函数采用pathmap作为参数。它返回一个新的映射,其路径由 at 符号 ("@").

运行代码

最后,您需要调用函数。这可以从Python交互式解释器.

以下代码将运行该算法并显示漂亮的输出:

>>>
>>> path = find_path(map)
>>> print(show_path(path, map))
@@.....X..
..@....X..
...@XXXX..
....@@@@@.
.........@

首先你得到最短路径find_path()。然后你把它传递给show_path()渲染带有标记路径的地图。最后,你print()映射到标准输出。

路径向右移动一步,然后向右下角移动几步,然后向右移动几步,最后以向右下角的对角一步结束。

恭喜!您已经使用 Python 解决了一个问题heapq模块。

这些类型的寻路问题可以通过动态规划和优先级队列的组合来解决,在以下领域很常见:工作面试和编程挑战。例如,2019 年代码问世包含一个问题这可以通过此处描述的技术来解决。

结论

你现在知道什么是优先队列数据结构是什么以及它们可用于解决哪些类型的问题。您学习了如何使用 Pythonheapq使用 Python 列表作为堆的模块。您还学习了如何使用 Python 中的高级操作heapq模块,如merge(),它在内部使用堆。

在本教程中,您学习了如何:

  • 使用低级函数在Python中heapq用于解决需要堆或优先级队列的问题的模块
  • 使用高级功能在Python中heapq用于合并排序的可迭代对象或查找可迭代对象中最大或最小元素的模块
  • 认出问题堆和优先级队列可以帮助解决
  • 预测表现使用堆的代码

凭借您对堆和 Python 的了解heapq模块,您现在可以解决许多问题,其中解决方案取决于找到最小或最大元素。要按照您在本教程中看到的示例进行操作,您可以从以下链接下载源代码:

获取源代码: 单击此处获取您将使用的源代码了解本教程中的 Python heapq 模块。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Python heapq 模块:使用堆和优先级队列 的相关文章

随机推荐

  • 从生成的类名称中删除 SWIGTYPE

    是否有办法从生成的类名中删除 SWIGTYPE 部分并替换为另一个字符串文字 即 将 SWIGTYPE p ex session java 更改为 ex session java 剥离生成的 SWIGTYPE p SWIG i 文件 mod
  • 检查字符串是否 x% 相等

    我想比较三个字符串 a text string target string a kind of similar text string the cow jumps over the moon 并设置一个百分比参数 返回与目标相似 x 的结果
  • 在 Flutter 中使用 http.post 和注册表单上传图片?

    所以我想将文件 图像 与一堆其他变量 字符串 上传到服务器 字符串 名字 姓氏 生日 电话 地址 文件图像 return http post uri headers Accept application json Authorization
  • 获取每组分组结果的前n条记录

    以下是最简单的示例 尽管任何解决方案都应该能够扩展到所需的 n 个顶级结果 给定如下表 其中包含人员 组和年龄列 您会如何找出每组中年龄最大的 2 个人 组内的平局不应产生更多结果 但应按字母顺序给出前 2 个结果 Person Group
  • 通过在tableview iPhone中选择tableview行添加动态子行错误?

    我想动态添加行 我有建筑物名称的表格视图列表 如果有人选择建筑物 didSelectRowAtIndexPath 则建筑物的相应楼层应动态添加为子行 这就像最大化和最小化相应建筑列表选择上的子行一样 我该怎么做呢 提前致谢 NSIntege
  • Vue.js 3 - 在 vue 中使用 Vue I18n 插件时出错 - 无法设置未定义的属性“_vm”

    我刚刚开始使用 Vue js 并通过一些在线代码片段和教程来学习它 我正在尝试为我的 vue 项目实现国际化支持 但我在网络控制台中遇到错误 这是我的代码片段 main js import createApp from vue import
  • tkinter 图标可以使用哪些文件格式?

    我知道这可能很明显 但在 tkinter 中你可以设置一个图标 但我发现很难找到一个图标 我只是想知道你是否必须使用 ico文件的格式或者是否有办法使用 png or jpeg files 目前我有 window Tkinter Tk wi
  • 处置 Angular 2 应用程序或组件

    有没有办法处理 angular2 应用程序或组件 我正在做一个混合的 ASP NET MVC 和 Angular 2 应用程序 其中 ASP NET MVC 加载部分视图 包括 Angular 2 应用程序 现在 我需要为每个部分视图提供一
  • 基础 SDK 3.0 到 SDK 4.0

    我安装了 SDK 4 0 发现无法访问 3 2 之前的所有 SDK 版本 我找到了下载 SDK 3 1 3 的链接 因此我有两个 dmg 安装文件 问题 SDK 4 0 不允许访问早期版本吗 如果上面的答案是否定的 那么这是否意味着我必须安
  • 如何使用实体框架在连接表中插入数据?

    我是实体框架的新手 我希望获得一些帮助来在 连接表 中插入数据 我有三张桌子 Profiles Tags和一个叫ProfilesTags连接这两个表 类是从数据库 DB First 自动生成的 public partial class Pr
  • jQuery 函数声明说明

    我已经打开 jQuery 1 7 1 库并想研究代码 但我发现函数以奇怪的方式声明 对我来说 例如 show function some code here 我学会了这样定义函数 function show some code here 有
  • 如何使用 Happy 获得漂亮的语法错误消息?

    我目前正在使用快乐的解析器生成器 其他解析器生成器可以给出不错的消息 例如 意外的结束行 预期的 然后 很高兴我只得到当前的令牌和错误的位置 您能给我一个如何获取上述错误消息的示例吗 我为此目的编写了一个 Happy 功能 请参阅我的博文
  • SessionNotCreatedException:消息:会话未创建:此版本的 ChromeDriver 仅支持带有 Selenium ChromeDriver 的 Chrome 版本 76

    我目前使用的是 Chrome 75 并且我已经下载了兼容 Chrome 驱动程序对于Linux 我还将它添加到 PATH 变量中 但是 当我尝试使用以下命令初始化驱动程序时driver webdriver Chrome 我收到以下错误 se
  • Python eval():动态计算表达式

    目录 Understanding Python s eval 第一个参数 表达式 第二个参数 全局变量 第三种说法 本地人 Evaluating Expressions With Python s eval 布尔表达式 数学表达式 通用表达
  • 养成习惯

    学习 Python 编程等复杂技能是很困难的 根据我的经验 提高技能的追求永远不会停止 成功的最好方法是养成每天或每周的习惯努力提高你的技能 在本视频中 您将看到一些有关 Real Python 如何帮助您实现这一目标的提示 恭喜 该视频也
  • 该做什么和不该做什么:Python 编程建议

    本课程涵盖 PEP8 提供的更多建议 以保持一致性并消除歧义 在本课程结束时 您将知道如何检查 如果一个布尔值是True或者False 列表是否为空 变量是否具有定义的值 该参数不是 None 列表中的后缀和前缀
  • 如何设置 Django 项目

    当您开始构建任何新的 Django Web 应用程序时 您需要首先解决一个基本设置 本课程概述了设置 Django 项目的必要步骤 在本课程中 您将重点关注启动新的 Web 应用程序所需执行的初始步骤 您应该首先安装Python 并且应该知
  • 后续步骤:创建博客

    在本课程中 您构建了 Django 组合 了解了很多有关 Django 功能的知识 并与很多错误消息交了朋友 如果您想继续构建这个项目 您可以查看本课程基于的文章添加一个博客到您的网站 博客是与世界分享您的技术知识和写作技巧的好方法 下载
  • 了解 Python 模拟对象库

    目录 什么是模拟 Python 模拟库 The Mock Object 惰性属性和方法 断言和检查 管理模拟的返回值 管理模拟的副作用 配置你的模拟 patch patch 作为装饰器 patch 作为上下文管理器 修补对象的属性 在哪里打
  • Python heapq 模块:使用堆和优先级队列

    目录 What Are Heaps 数据结构 堆和优先级队列 堆的实现 优先级队列的用途 Heaps as Lists in the Python heapq Module 基本操作 高级别操作 堆可以解决的问题 如何发现问题 Exampl