Python 优先级队列(分步指南)

2023-10-22

队列是一种按称为 FIFO 的顺序检索数据项的数据结构(先进先出)。在 FIFO 中,第一个插入的元素将首先从队列中弹出。
优先级队列是队列数据结构的高级版本。

具有最高优先级的元素被放置在优先级队列的最顶部,并且是第一个被出列的元素。

有时,队列包含具有相同优先级的项目;因此,项目将按照其在队列中的顺序出列,就像 FIFO 一样。

在Python中,有多种选项来实现优先级队列。这queuePython 中的标准库支持优先级队列。

同样,heapqPython 中的模块还实现了优先级队列。我们还可以使用列表、元组, and dict 模块来实现优先级队列。

在本教程中,您将学习如何创建优先级队列以及可以对优先级队列中的元素执行的各种其他操作。

 

 

为什么要优先队列?

优先级队列在计算机世界中有很多应用。例如:

  • 操作系统使用优先级队列在不同计算单元之间平衡或分配负载(任务集)。这使得处理高效,从而引入并行计算。
  • 优先级队列用于操作系统中的中断处理。
  • 在人工智能中,优先级队列实现了A*搜索算法。它跟踪未探索的路线并找到图的不同顶点之间的最短路径。路径长度越小,其优先级最高。
  • 实施时Dijkstra 算法,优先级队列有效地找到矩阵或邻接列表图中的最短路径。
  • 优先级队列对堆进行排序。堆是优先级队列的一种实现。

 

如何在Python中创建优先级队列?

优先级队列中的元素总是包含key and a value。键量化了元素的优先级。

使用列表:

使用列表实现优先级队列非常简单。只需创建一个列表,追加元素(键、值),并在每次追加元素时对列表进行排序。

Code:


employees = []
employees.append((1, "Andrew"))
employees.append((4, "John"))
employees.sort(reverse = True)
employees.append((3, "Jean"))
employees.sort(reverse = True)
employees.append((2, "Matt"))
employees.sort(reverse = True)
while employees:
    print(employees.pop())

  

当第一个元素被追加到列表中时,不需要对列表进行排序。优先队列的列表实现效率不高,因为在每个新条目之后都需要对列表进行排序。因此,需要时间根据元素的优先级来维护元素的顺序。

Output:

使用元组

Python 元组和列表在某种程度上是相同的。列表和元组都是Python的有序数据结构,并且允许重复值。但是列表的元素是可变的,而元组的元素是不可变的。

要使用元组实现优先级队列,我们​​将首先使用优先级队列的元素创建一个元组,然后对该元组进行排序。

由于您无法更改元组中的元素,因此元组不提供像列表一样的常规排序功能。要对元组进行排序,我们需要使用排序函数。

排序和排序方法之间的区别在于,排序方法不返回任何内容,而是更改列表的实际顺序。

而排序函数始终返回排序后的序列,并且不会干扰元组的实际序列。
在下面的代码行中,我们将创建一个元组并使用元组实现优先级队列:


mytuple = ((1, "bread"), (3, "pizza"), (2, "apple"))
  

现在让我们使用sorted()方法对元组进行排序:


sorted(mytuple)
  

 

Output:

使用字典

在 Python 字典中,数据以键和值对的形式存储。我们将使用键作为元素的优先级编号,使用值作为队列元素的值。

这样,我们就可以使用默认的Python字典来实现优先级队列。
创建字典并添加项目(键和值):


mydict = {2: "Asia", 4: "Europe", 3: "America", 1: "Africa"}
  

创建字典后,您需要按键对其项目进行排序。我们需要使用字典 items() 方法将字典的项目存储到变量中:


dict_items = mydict.items()
  

现在使用sorted()函数并打印排列好的优先级队列:


print(sorted(dict_items))  

 

Output:

要从字典优先级队列中弹出项目,您可以使用波皮特姆()方法。字典 popitem() 方法将使具有最高优先级的元素出列:


mydict = {2: "Asia", 4: "Europe", 3: "America", 1: "Africa"}
mydict.popitem()
print(mydict)

  

 

Output:

使用队列模块

让我们使用内置的优先级队列创建一个queuePython 中的模块。使用队列模块是优先级队列最简单的用法。

Code:


import queue
p_queue = queue.PriorityQueue()
p_queue.put((2, "A"))
p_queue.put((1, "B"))
p_queue.put((3, "C"))

  

在此代码中,构造函数 PriorityQueue() 创建一个优先级队列并将其存储在变量 p_queue 中。 PriorityQueue 类的 put(priority_number, data) 函数在队列中插入一个项目。

put(priority_number, data) 函数有两个参数:第一个参数是一个整数,用于指定队列中元素的优先级编号,第二个参数是要插入到队列中的元素。
要从队列中弹出并返回项目,请使用 get() 函数:


print(p_queue.get())
  

如您所见,所有项目均已出队。要检查队列中是否存在任何元素,请使用empty()函数。 empty() 函数返回一个布尔值。如果返回true,则表示队列为空。


p_queue.empty()
  

 

使用heapdict

The heapdict模块类似于 Python 中的常规字典,但在 heapdict 中,您可以弹出项目,也可以更改优先级队列中项目的优先级。

使用heapdict,您可以更改项目的优先级:即增加或减少项目的键。
默认情况下不安装 heapdict 模块。要安装 heapdict:


pip install heapdict
  

现在我们来实现优先级队列:

Code:


import heapdict
hd = heapdict.heapdict()
hd['pen'] = 3
hd['notebook'] = 1
hd['bagpack'] = 4
hd['lunchbox'] = 2
while hd:
	print(hd.popitem())
	
  

Output:

使用堆q

计算机世界中的堆数据结构主要是为了实现优先级队列。 Python中的heapq模块可以用来实现优先级队列。

Code:


import heapq
employees = []
heapq.heappush(employees, (3, "Andrew"))
heapq.heappush(employees, (1, "John"))
heapq.heappush(employees, (4, "Jean"))
heapq.heappush(employees, (2, "Eric"))
while employees:
	print(heapq.heappop(employees))	
  

Output:

在此代码中,创建了一个堆并将元素(优先级键、值)推入堆中。
The heapq模块默认实现最小堆。具有最小键的元素被认为在最小堆中具有最高优先级。

因此,无论元素排队的顺序如何,最小的元素都会首先弹出,如上面的输出屏幕所示。

每当有元素被压入或弹出时,heapq 模块都会维护堆结构本身。
本教程将使用优先队列的 heapq 实现。

 

优先队列与最小堆

优先级队列是堆的实现。因此,这个实现可以是最大堆或最小堆。如果优先级队列的实现是最大堆,那么它将是最大优先级队列。

同样,如果实现是最小堆,那么优先级队列将是最小优先级队列。

在最小堆中,最小的节点是二叉树的根。
优先级队列和最小堆是相同的。唯一的区别是,在优先级队列中,元素的顺序取决于元素的优先级编号。

 

获取索引处的值

我们可以使用优先级队列的堆实现来获取索引处的值。首先创建一个堆,然后将项目推入堆中。优先级队列中的项目将有一个键和一个值。

这个键不是堆的索引。该键量化了优先级。索引是存储优先级队列的项(键、值)的位置。
考虑下面的例子:

Code:


import heapq
employees = []
heapq.heappush(employees, (3, "Andrew"))
heapq.heappush(employees, (1, "John"))
heapq.heappush(employees, (4, "Jean"))
heapq.heappush(employees, (2, "Eric"))
print("Value at index 0: ", employees[0])
print("Value at index 3: ", employees[3])
  

Output:

 

删除一个元素

要从优先级队列中删除元素,只需弹出该元素即可。具有最高优先级的元素将从队列中出列并删除。
创建队列:

Code:


import heapq
hq = []
heapq.heappush(hq, (3, "Jean"))
heapq.heappush(hq, (2, "Eric"))
heapq.heappush(hq, (4, "Monica"))
heapq.heappush(hq, (1, "Joey"))
heapq.heappop(hq)	
  

Output:

 

更新优先级和值

要更新优先级队列中的优先级,请获取要更新优先级的元素的索引,并为该元素分配一个新键。

您也可以更改元素的值。查看下面的代码:

Code:


import heapq
hq = []
heapq.heappush(hq, (3, "Jean"))
heapq.heappush(hq, (2, "Eric"))
heapq.heappush(hq, (4, "Monica"))
heapq.heappush(hq, (1, "Joey"))
print(hq)
hq[1] = (6, 'Eric')
print(hq)
heapq.heapify(hq)
print(hq)
  

Output:

更新元素的优先级后,我们需要对堆进行heapify来维护堆数据结构。这堆化()heapq 模块的方法将 Python 可迭代对象转换为堆数据结构。

 

替换一个元素

在优先级队列的堆实现中,您可以弹出具有最高优先级的项目,并同时推送新项目,这意味着您正在用新的项目替换最高优先级的项目。

这是在以下人员的帮助下完成的heapq函数称为堆替换:


heapq.heapreplace(heap, item)
  

您将传递队列以从中弹出项目,并传递新项目以添加到队列中。

Code:


import heapq
hq = []
heapq.heappush(hq, (3, "Jean"))
heapq.heappush(hq, (2, "Eric"))
heapq.heappush(hq, (4, "Monica"))
heapq.heappush(hq, (1, "Joey"))
heapq.heapify(hq)
print(hq)
heapq.heapreplace(hq, (6, "Ross"))
print(hq)

  

Output:

The 堆替换()函数将具有最高优先级的元素出队并将新元素添加到队列中。新元素的优先级最低。因此,它被放入队列的最后。

The heapq模块还提供了一个方法称为heappushpop(堆,项目)。 heappushpop(heap, item) 结合了 heappop() 和 heappush() 方法的功能。

heappushpop() 方法提高了效率,并且比使用单独的函数推送和弹出元素花费的时间更少。

heapreplace() 和 heappushpop() 之间的区别在于 heapreplace() 先弹出项目,然后将项目推入队列,这是替换元素的实际定义。

而 heappushpop() 将一个项目推送到队列中,更改队列的大小,然后将最小(最高优先级)的元素弹出。

Code:


import heapq
heap = []
heapq.heappush(heap, (3, "Africa"))
heapq.heappush(heap, (2, "America"))
heapq.heappush(heap, (1, "Asia"))
heapq.heappush(heap, (4, "Europe"))
heapq.heappushpop(heap, (5, "Antarctica"))
while heap:
	heapq.heappop(heap)

  

Output:

 

查找热门项目而不删除

要查找队列中最靠前的项目而不弹出它们,heapq提供了一个函数叫做n最大(n,堆).
此函数返回优先级队列中的前 n 个项目。

Code:


import heapq
heap = []
heapq.heappush(heap, (3, "eat"))
heapq.heappush(heap, (1, "study"))
heapq.heappush(heap, (2, "rest"))
heapq.heappush(heap, (4, "sleep"))
heapq.nlargest(3, heap)
print(heap)
  

Output:

从输出中可以看出,当以下情况时,将返回优先级队列顶部的项目:n最大()使用了函数。请注意,该函数仅返回项目,并且不会将项目出列,如打印命令所示。

 

查找底部项目而不删除

要查找优先级队列底部的项目而不弹出它们,heapq提供了一个函数叫做n最小(n,堆)。此函数返回优先级队列底部的 n 个项目。

Code:


import heapq
heap = []
heapq.heappush(heap, (3, "eat"))
heapq.heappush(heap, (1, "study"))
heapq.heappush(heap, (2, "rest"))
heapq.heappush(heap, (4, "sleep"))
heapq.nsmallest(3, heap)
print(heap)
  

Output:

从输出中可以看出,当n最小()使用了函数。请注意,该函数仅返回项目,并且不会将项目出列,如打印命令所示。

 

带有自定义比较器的 Python 优先级队列

自定义比较器用于比较两个用户定义的可迭代对象。在Python优先级队列中,可以使用自定义比较器根据用户定义的值对队列进行排序。

例如,我们使用heapq创建一个优先级队列。然后我们使用sorted()方法对heapq进行排序。

它会根据元素的键(优先级编号)对队列中的元素进行排序。考虑下面的例子:

Code:


import heapq
heap = []
heapq.heappush(heap, (3, "eat"))
heapq.heappush(heap, (1, "study"))
heapq.heappush(heap, (2, "rest"))
heapq.heappush(heap, (4, "sleep"))
print(sorted(heap))

  

Output:

现在让我们根据自定义比较器对队列进行排序。我们希望以这样的方式排列队列中的元素,即对队列进行排序后,值按字母顺序排列。

为此,我们将使用 lambda 函数。 lambda 函数是一种小型匿名函数,由一个带有任意数量参数的表达式组成。

lambda 函数或 lambda 表达式返回一个可以在程序中的任何位置使用的值。

Code:


import heapq
heap = []
heapq.heappush(heap, (3, "eat"))
heapq.heappush(heap, (1, "study"))
heapq.heappush(heap, (2, "rest"))
heapq.heappush(heap, (4, "sleep"))
print(sorted(heap, key=lambda heap: heap[1]))
  

Output:

在此示例中,lambda 表达式指示根据值(而不是键)按字母顺序对队列进行排序。 Sorted() 方法采用三个参数:

  • The iterable: 待排序的序列
  • Key: 该键是可选的。它被认为是排序比较的基础。关键是用户定义的比较器功能。
  • Reverse:反转是布尔值。如果设置为 true,则会反转排序顺序。默认情况下,反向参数为 false,这意味着它将按升序对序列进行排序。如果reverse设置为true,则序列将按降序排列。

 

反转优先队列顺序

要反转优先级队列的顺序,请使用sorted()方法对队列进行排序,并设置reverse论证为真。默认情况下,队列按升序排序。

If the reverse参数设置为 true,它将按降序更改序列,如下例所示:

Code:


import heapq
heap = []
heapq.heappush(heap, (3, "Africa"))
heapq.heappush(heap, (1, "America"))
heapq.heappush(heap, (2, "Asia"))
heapq.heappush(heap, (4, "Europe"))
print(sorted(heap, reverse=True))
  

Output:

 

重复键(同等优先级)

如果优先级队列中的元素有重复的键,则意味着这些元素的优先级相同。但问题是哪个元素将首先出列?

那么,位于队列顶部的元素将首先从队列中出列。

Code:


import heapq
heap = []
heapq.heappush(heap, (3, "Africa"))
heapq.heappush(heap, (2, "America"))
heapq.heappush(heap, (1, "Asia"))
heapq.heappush(heap, (1, "Europe"))
while heap:
	print(heap.pop())	
  

Output:

 

打破平局

当存在具有相同优先级的元素时,就会出现优先级队列中的平局。当两个元素不可比较时,即比较器在比较队列中的 a 和 b 元素后返回 0。

在这种情况下,优先级队列必须决定哪个元素首先出队。

这就是所谓的打破平局。
如果出现平局,我们可以在优先级队列中实现 FIFO(先进先出)或 LIFO(后进先出)。

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

Python 优先级队列(分步指南) 的相关文章

随机推荐

  • 如何在 Debian 10 Linux 上添加交换空间

    交换空间是磁盘上的空间 当物理内存已满时使用 当 Linux 系统耗尽 RAM 时 非活动页面将从 RAM 移至交换空间 交换空间可以采用专用交换分区或交换文件的形式 通常 运行 Debian 虚拟机时不存在交换分区 因此唯一的选择是创建交
  • 如何在 CentOS 8 上安装 Apache Maven

    Apache Maven 是一个开源项目管理和理解工具 主要用于 Java 项目 Maven 使用项目对象模型 POM 它本质上是一个 XML 文件 其中包含有关项目 配置详细信息 项目依赖项等的信息 在本教程中 我们将解释如何在 Cent
  • 如何在 Ubuntu 18.04 上安装 TensorFlow

    TensorFlow是由 Google 构建的免费开源机器学习平台 许多组织都在使用它 包括 Twitter PayPal 英特尔 联想和空中客车公司 TensorFlow 可以在 Python 虚拟环境中安装在系统范围内 作为Docker
  • Linux 中的 Chattr 命令(文件属性)

    在 Linux 中 文件属性是描述文件行为的元数据属性 例如 属性可以指示文件是否被压缩或指定文件是否可以被删除 一些属性 如不变性 可以设置或清除 而其他属性 如加密 是只读的 只能查看 对某些属性的支持取决于所使用的文件系统 本文介绍了
  • 在Ubuntu上安装RPM包

    Ubuntu 存储库包含数千个 deb 软件包 可以从 Ubuntu 软件中心或使用apt命令行实用程序 Deb 是所有基于 Debian 的发行版 包括 Ubuntu 都使用的安装包格式 有些软件包在标准 Ubuntu 存储库中不可用 但
  • 如何检查PHP版本

    PHP 是最常用的服务器端编程语言之一 PHP 版本之间存在一些重要差异 因此在某些情况下可能需要了解您的服务器上运行的是哪个版本 例如 如果您在开始安装之前升级应用程序或安装需要特定 PHP 版本的新应用程序 则需要找出 PHP 服务器的
  • 如何在 Debian 10 Linux 上安装 Google Chrome 网络浏览器

    谷歌浏览器是世界上最流行的网络浏览器 它是专为现代网络打造的快速 直观且安全的浏览器 Chrome 不是开源浏览器 并且不包含在官方 Debian 存储库中 它是基于Chromium 一个开源浏览器 可在默认 Debian Buster 存
  • 如何在 Ubuntu 中将用户添加到 Sudoers

    sudo是一个命令行程序 允许受信任的用户以 root 或其他用户身份执行命令 在本文中 我们将向您展示两种向用户授予 sudo 权限的方法 第一个是将用户添加到sudoers 文件 该文件包含控制向哪些用户和组授予 sudo 权限以及权限
  • 检查 gzip 文件而不解压缩:zcat、zless 和 zmore

    Linux 提供了多个用于处理压缩文件的命令 例如 zcat zless 和 zmore 本教程将深入探讨这些命令的用法 让您可以导航和检查压缩文件 而无需解压缩它们 下表总结了这 3 种工具之间的差异 Tool Description P
  • 使用 source 命令在 Linux 中获取脚本

    The sourceLinux 中的 command 是一个内置的 shell 命令 用于从文件中读取和执行命令 这意味着脚本定义的任何变量或函数在脚本执行完成后仍然可用 现在 让我们开始探索它的功能source命令 目录 hide 1 子
  • Linux 上的 MySQL(初学者教程)

    在这篇文章中 我们将介绍 Linux 上 MySQL 的许多方面 首先 如何安装它 如何执行基本的 CRUD 操作 如何导入和导出数据 如何使用 MySQL 引擎本身 例如设置 root 用户密码 等等 MySQL 是世界上最流行的关系数据
  • 将 NumPy 数组转换为 Pandas DataFrame(15+ 场景)

    通常我们需要在 NumPy 数组中创建数据并将其转换为 DataFrame 因为我们必须处理 Pandas 方法 在这种情况下 转换NumPy 数组 ndarrays 到数据框使我们的数据分析变得方便 在本教程中 我们将仔细研究一些可用于将
  • 使用 Python 发送电子邮件(多个示例)

    Python 允许您使用其内置模块自动执行发送电子邮件的任务 这样做可以让您摆脱手动向数千名用户发送电子邮件的繁琐且耗时的任务 本教程将探讨一些快速 简单的发送电子邮件和使用 Python 内置电子邮件模块的方法 目录 hide 1 检查电
  • Python 中的深度优先搜索算法(多个示例)

    深度优先搜索是一种流行的图遍历算法 在本教程中 我们将通过示例了解它的工作原理 以及我们如何用 Python 实现它 我们将研究以下部分 目录 hide 1 介绍 2 深度优先搜索算法 3 Representing a graph
  • Python NumPy 数组教程

    NumPy 是一个 Python 库 模块 用于科学计算Python编程 在本教程中 您将学习如何对 NumPy 数组执行多种操作 例如以多种方式添加 删除 排序和操作元素 NumPy 提供多维数组对象和其他派生数组 例如屏蔽数组或屏蔽多维
  • 关于 Linux 导出命令您需要了解的一切

    The exportLinux中的命令是一个内置的shell命令 用于设置环境变量在当前 shell 会话中 通过标记变量或函数以便随后导出到子进程的环境中 export命令确保这些变量对子进程的可用性 目录 hide 1 导出命令的语法
  • 安装、配置和使用 Linux NIS 服务器

    我们使用 Linux NIS 服务器 网络信息服务 用于在网络上的系统之间共享存储在平面文件中的关键数据 通常理想的做法是使用共享存储库 例如 NIS 来存储用户和组信息 而不是将它们存储在 etc passwd 等平面文件中 那么这样做有
  • NumPy Meshgrid 从零到英雄

    Python 的 NumPy是处理数组 矩阵数据最常用的库 矩阵可以被视为二维值 网格 其中网格中每个值的位置由一对值 i j 给出 这些值表示该值在网格中的行号和列号 在本教程中 我们将了解如何使用 Python 中的 NumPy 库创建
  • Python 中的快速排序算法(逐步)

    在编程世界中 大多数问题的答案都可以在存储在各种数据结构中的数据中并借助一些标准算法找到 今天 我们将讨论快速排序算法以及如何在 Python 中实现它 在开始确定这些答案之前 您将需要一组数据 在许多情况下是排序数据 来执行进一步的计算
  • Python 优先级队列(分步指南)

    队列是一种按称为 FIFO 的顺序检索数据项的数据结构 先进先出 在 FIFO 中 第一个插入的元素将首先从队列中弹出 优先级队列是队列数据结构的高级版本 具有最高优先级的元素被放置在优先级队列的最顶部 并且是第一个被出列的元素 有时 队列