IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 算法设计与分析——堆(四):优先队列 -> 正文阅读

[数据结构与算法]算法设计与分析——堆(四):优先队列

分类目录:《算法设计与分析》总目录
相关文章:
·堆(一):基础知识
·堆(二):维护堆的性质
·堆(三):建堆
·堆(四):优先队列
·排序算法(三):堆排序


本文我们要介绍堆的一个常见应用:作为高效的优先队列。和堆一样,优先队列也有两种形式:最大优先队列最小优先队列。这里我们关注于如何基于最大堆实现最大优先队列,但是在本博客主要采用的Python语言中,用heapq模块构建的堆是最小堆。

优先队列是一种用来维护由一组元素构成的集合 S S S的数据结构,其中的每一个元素都有一个相关的值,称为关键字。一个最大优先队列支持以下操作:

  • heap_insert(S, x):把元素 x x x插入集合 S S S中。这一操作等价于 S = S ∪ { x } S=S\cup \{x\} S=S{x}
  • heap_maximum(S):返回 S S S中具有最大键字的元素。
  • heap_extract_max(S):去掉并返回 S S S中的具有最大键字的元素。
  • heap_increase_key(S, i, k):将元素 x x x的关键字值增加到 k k k,这里假设 k k k的值不小于 x x x的原关键字值。

最大优先队列的应用有很多,其中一个就是在共享计算机系统的作业调度。最大优先队列记录将要执行的各个作业以及它们之间的相对优先级。当一个作业完成或者被中断后,调度器调用extract_max(s)从所有的等待作业中,选出具有最高优先级的作业来执行。在任何时候,调度器可以调用heap_insert(S, x)把一个新作业加入到队列中来。

相应地,最小优先队列支持的操作也包括插入、最小元素、去掉并返回最小元素等。最小优先队列可以被用于基于事件驱动的模拟器。队列中保存要模拟的事件,每个事件都有一个发生时间作为其关键字。事件必须按照发生的时间顺序进行模拟,因为某一事件的模拟结果可能会触发对其他事件的模拟。在每一步,模拟程序调用extract_min(S)来选择下一个要模拟的事件。当一个新事件产生时,模拟器通过调用heap_insert(S, x)将其插入最小优先级队列中。

显然,优先队列可以用堆来实现。对一个像作业调度或事件驱动模拟器这样的应用程序来说,优先队列的元素对应着应用程序中的对象。通常,我们需要确定哪个对象对应一个给定的优先队列元素,反之亦然。因此,在用堆来实现优先队列时,需要在堆中的每个元素里存储对应对象的句柄。句柄(如一个指针或一个整型数等)的准确含义依赖于具体的应用程序。同样,在应用程序的对象中,我们也需要存储一个堆中对应元素的句柄。通常,这一句柄是数组的下标。由于在堆的操作过程中,元素会改变其在数组中的位置,因此,在具体的实现中,在重新确定堆元素位置时,我们也需要更新相应应用程序对象中的数组下标。因为对应用程序对象的访问细节强烈依赖于应用程序及其实现方式,所以这里我们不做详细讨论。需要强调的是,这些句柄也需要被正确地维护。
现在,我们来讨论如何实现最大优先队列的操作。过程heap_maximum(S)可以在 Θ ( 1 ) \Theta(1) Θ(1)时间内实现找出最大元素操作。

def heap_maximum(S):
	return S[0]

过程heap_extract_max(S)实现去掉并返回 S S S中的具有最大键字的元素的操作。它与heapSort(arr)过程中的for循环体部分很相似。

def heap_extract_max(S):
	max = heap_maximum(S)
	A[0] = A[len(A) - 1]
	max_heap(A)
	return max

heap_extract_max(S)的时间复杂度为 O ( lg ? n ) O(\lg n) O(lgn)。因为除了时间复杂度为 O ( lg ? n ) O(\lg n) O(lgn)max_heap(A)以外,它的其他操作都是常数阶的。

heap_increase_key(S, i, k)能够实现将元素 x x x的关键字值增加到 k k k,这里假设 k k k的值不小于 x x x的原关键字值操作。在优先队列中,我们希望增加关键字的优先队列元素由对应的数组下标 i i i来标识。这一操作需要首先将元素 A [ i ] A[i] A[i]的关键字更新为新值。因为增大 A [ i ] A[i] A[i]的关键字可能会违反最大堆的性质,所以上述操作采用了类似于《排序算法(一):插入排序》中插入循环的方式,在从当前结点到根结点的路径上,为新增的关键字寻找恰当的插入位置。在heap_increase_key(S, i, k)的操作过程中,当前元素会不断地与其父结点进行比较,如果当前元素的关键字较大,则当前元素与其父结点进行交换。这过程会不断地重复,直到当前元素的关键字小于其父结点时终止,因为此时已经重新符合了最大堆的性质。

def heap_increase_key(S, i, k):
	if k < A[i]:
		print('New key is smaller than current key.')
		return k
	A[i] = k
	while i > 0 and A[parent(i)] < A[i]:
		A[i], A[parent(i)] = A[parent(i)], A[i]
		i = parent(i)

下图显示了heap_increase_key(S, i, k)的一个操作过程。在包含 n n n个元素的堆上,heap_increase_key(S, i, k)的时间复杂度是 O ( lg ? n ) O(\lg n) O(lgn)
更新堆中的元素
同理,heap_insert(S, x)能够实现把元素 x x x插入集合 S S S中的操作。它的输入是要被插入到最大堆 S S S中的新元素的关键字。heap_insert(S, x)首先通过增加一个关键字为 A [ l e n ( A ) ? 1 ] ? 1 A[len(A) - 1] - 1 A[len(A)?1]?1的叶结点来扩展最大堆。然后调用heap_increase_key(S, len(A), x)为新结点设置对应的关键字,同时保持最大堆的性质。

def heap_insert(S, x):
	A[len(A)] = A[len(A) - 1] - 1
	heap_increase_key(S, len(A), x)

综上所述,在一个包含 n n n个元素的堆中,所有优先队列的操作都可以在 O ( lg ? n ) O(\lg n) O(lgn)时间内完成。

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-08-30 12:27:46  更:2021-08-30 12:27:54 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 23:21:51-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码