优先级队列数据结构的术语?
Posted
技术标签:
【中文标题】优先级队列数据结构的术语?【英文标题】:Terminology for a priority queue data structure? 【发布时间】:2018-04-12 01:45:28 【问题描述】:我一直在使用原始开发人员称为heap
的数据结构,它用于实现优先级队列。
虽然有很多关于二叉树的文章,但(最小/最大)堆的定义似乎不太明确(细节因实现而异)。
我注意到的一些特征不一定适用于二叉树结构。
同一元素可以多次出现在队列中,而不会导致执行或实施复杂化。 搜索(虽然可能并且比穷举搜索更快),但效率不高(因为不必平衡每个节点的子元素)。 由于搜索效率不高且可能出现重复,删除可能需要存储对node
的引用,而不是使用key
来查找节点(这是二叉树的常见做法) .
Changing priorities in the heap is trivial,与二叉树相比 where it's most common to delete+insert。(与二叉树相比,最佳情况更好,最坏情况更差)
是否有与这些特征相匹配的数据结构的更详细的术语?或者它只是一个 min/max heap
,恰好被用作 priority-queue
?
注意,这里有一个指向具有上述特征的min-heap 的链接。
【问题讨论】:
【参考方案1】:binary heap 是priority queue 抽象数据结构的具体实现。它很受欢迎,因为它易于实现、内存高效且相当快:O(log n) 插入和 O(log n) 删除根(最小堆中最小,最大堆中最大)元素。大多数实现还提供了一种 peek 方法,该方法允许在不删除根元素的情况下查看它。
二叉堆并没有做任何其他特别好的事情。与您的观察相反,在二进制堆中查找特定项目需要顺序扫描。尽管节点是有序的(未排序),但这种顺序并不适合搜索。
二叉堆的典型实现是在一个数组中。由于 shape 属性(该结构可以被视为完美(或完全)二叉树),这意味着父子关系被隐式表示。项目按广度优先顺序存储在数组中。
正如用户 templatetypedef 在他的回答中指出的那样,二叉堆是一种特定类型的二叉树,不应与二叉搜索树混淆,二叉搜索树专为快速插入和删除项目以及定位项目而设计按键。
虽然更改堆中项目的优先级或从堆中删除任意项目非常容易,但您指出的问题是定位要操作的项目。在典型的二叉堆中,查找要修改的项目需要顺序搜索。如果您需要在堆中移动项目的能力,您通常会将二进制文件与由项目的键索引的字典或哈希映射结合起来。该值是数组中项目的索引。每次移动项目时都会更新该索引。这会使堆操作减慢一个常数因子,但让您能够在 O(1) 中找到一个项目。
还有一种叫做Min-max heap 的东西,它是一种二进制堆,可以让您O(1) 访问最小和最大项。该实现与标准二进制最小堆的实现非常相似。
更令人困惑的是,还有 d-ary heap,它是一个每个节点包含两个以上子节点的堆。例如,三叉堆的每个节点有三个孩子。这些也是在带有隐式子指针的数组中实现的。
还有其他通常称为堆的数据结构,但实际上与堆并没有真正的关系,只是它们是优先级队列数据结构的不同实现。最流行的似乎是配对堆、斐波那契堆和二项式堆,所有这些都可以用二叉树来实现。 (同样,不是二叉搜索树。)
几年前,我在我的博客中写了一篇关于二进制堆(和 d-ary 堆)的指导性介绍。如果您有兴趣,请查看this entry,其中列出了该系列的所有文章。
【讨论】:
re: “与您的观察相反,在二进制堆中查找特定项目需要顺序扫描。虽然节点是有序的(未排序),但顺序并不适合自己进行搜索。” 我的观点是,当子节点的排序不可预测时,查找不会像平衡二叉树那样有效。 @ideasman42:你说,“搜索(尽可能比穷举搜索更快)......”我的意思是在二叉堆中搜索一个项目需要详尽的搜索。 由于父子节点在最小/最大堆中排序,搜索可以跳过超过搜索值的分支,即使两个分支都需要遍历,我认为这会比平均性能更好比较每个元素。 OTOH - 我无法想象在很多情况下这是一个好主意(也许搜索已知靠近堆顶部的值),否则 - 只需使用二叉搜索树。 @ideasman42:理论上,你是对的。在实践中,执行顺序扫描更容易,而且通常更快。 这意味着语句:“与您的观察相反,在二进制堆中查找特定项目需要顺序扫描” 不正确。【参考方案2】:我认为您混淆了二叉搜索树和二叉树。二叉树比其他任何东西都更像一种形状——它是一棵树,其中每个节点最多有两个孩子。节点不一定必须有值,如果有,也不需要遵守任何特定规则。
二叉搜索树是一棵二叉树,其中每个节点都持有一个键,每个节点都遵循左子树中的所有键都小于该节点的键且右子树中的所有键的规则子树大于节点的键。 (一些定义放宽了允许小于或等于而不是仅仅小于等的要求。)
还有许多其他数据结构是由不是 BST 的二叉树构建的。 k-d 树存储多维数据。二进制尝试存储位串。
所以我认为这里最好的描述是“二叉堆是完整的二叉树并且遵循堆属性,这与二叉搜索树不同,即使它们具有相同的底层形状(或多或少) 。”
【讨论】:
你的第一段似乎是正确的 AFAICS,但我认为库和软件开发的术语通常使用二叉树作为二叉搜索树的简写。所以我仍然不确定:en.wikipedia.org/wiki/Priority_queue#Usual_implementation 首先提到了heap
,然后在第二个示例中提到了二叉树。 heaps 上的页面将其称为binary-heap
,而不是binary-tree
,之所以提出这个问题,是因为我不确定这些术语是松散使用还是这里有严格的区别。
堆通常被认为是抽象数据类型,而不是数据结构,因为它几乎是可以使用树或树来实现同样好的类型的典型示例一个数组。
@chepner 我经常看到“堆”用作“二进制堆”的简写,特定数据类型,“优先队列”用作抽象数据类型。
@ideasman42 人们确实经常使用“二叉树”来表示“二叉搜索树”,但我想说这只是对术语的草率。 “优先级队列”是数据结构的一般类别,支持按优先级递增的顺序插入元素和出队,二叉堆(或简称“堆”)是一种可能的实现方式。
@templatetypedef 使用“堆”来指代一个实现同样草率。堆可以有效地使用数组来存储嵌入的“树”,其中元素i
的子元素是元素i/2
和i/2 + 1
,或者作为链接节点对象的树。优先级队列实际上是堆的应用程序。以上是关于优先级队列数据结构的术语?的主要内容,如果未能解决你的问题,请参考以下文章