std :: set和std :: priority_queue之间的区别
由于std::priority_queue
和std::set
(和std::multiset
)都是数据容器,它们存储元素并允许以有序方式访问它们,并具有相同的插入复杂度O(log n)
(或者,什么样的情况需要这个或那个?)?
虽然我知道潜在的结构是不同的,但我并不那么感兴趣,因为我在比较它们的性能和各种用途的适用性 。
注:我知道一套中没有重复。 这就是为什么我还提到了std::multiset
因为它具有与std::set
完全相同的行为,但是可以在允许存储的数据作为相等元素进行比较的情况下使用。 所以,请不要评论单个/多个密钥的问题。
一个优先级队列只允许你按照sorting顺序访问一个元素 – 也就是说,你可以得到最高优先级的项目,当你删除它时,你可以获得下一个最高优先级,依此类推。 优先级队列也允许重复的元素,所以它更像是一个多集而不是集。 Kopec指出,构build一个堆对于堆中的项目数量也是线性的,其中构build集合的地方是O(NlogN),除非它是从一个已经sorting的序列构build的(在这种情况下它也是线性的)。]
一个集合允许你按照sorting顺序完全访问,例如,你可以在集合的中间find两个元素,然后按照从一个到另一个的顺序遍历。
set / multiset通常由二叉树支持。 http://en.wikipedia.org/wiki/Binary_tree
priority_queue通常由一个堆支持。 http://en.wikipedia.org/wiki/Heap_(data_structure);
所以问题是什么时候应该使用二叉树而不是堆?
这两种结构都放在一棵树上,但是关于两者之间关系的规则是不同的。
我们将父母的职位P,左孩子的L,右孩子的R称为。
在二叉树L <P <R中
在一堆P <L和P <R
所以二叉树sorting“侧面”和堆sorting“向上”。
所以如果我们把它看作一个三angular形而不是二叉树L,P,R是完全sorting的,而在堆中L和R之间的关系是未知的(只有它们与P的关系)。
这具有以下效果:
-
如果你有一个没有sorting的数组,并且想把它变成一个二叉树,它需要
O(nlogn)
时间。 如果你想把它变成一堆,它只需要O(n)
时间,(因为它只是比较find极端的元素) -
如果你只需要极端元素(比较函数最低或最高),堆更有效率。 堆只做比较(懒惰),以确定极端的因素。
-
二叉树执行必要的比较,以便对整个集合进行sorting,并保持整个集合一直sorting。
-
堆具有最低元素的恒定查找(peek),二元树具有最低元素的对数时间查找。
std::priority_queue
允许执行以下操作:
- 插入一个元素
O(log n)
- 获得最小的元素
O(1)
- 删除最小的元素
O(log n)
而std::set
有更多的可能性:
- 插入任何元素
O(log n)
,常量大于std::priority_queue
- find任何元素
O(log n)
- find一个元素,> =比你正在寻找的
O(log n)
(lower_bound
) - 删除任何元素
O(log n)
- 按sorting顺序移至上一个/下一个元素
O(1)
- 获得最小的元素
O(1)
- 获得最大的元素
O(1)
由于
std::priority_queue
和std::set
(和std::multiset
)都是数据容器,它们存储元素并允许以有序方式访问它们,并具有相同的插入复杂度O(log n)
(或者,什么样的情况需要这个或那个?)?
尽pipe两个容器的插入和擦除操作都具有相同的复杂度O(log n) ,但这些std :: set的操作比std :: priority_queue慢。 这是因为std :: set使得许多内存分配。 std :: set的每个元素都存储在自己的分配中。 std :: priority_queue (默认使用std :: vector容器)使用单个分配来存储所有元素。 另一方面, std :: priority_queue在其元素上使用了许多交换操作,而std :: set只是使用指针交换。 所以如果交换元素types的操作非常慢,使用std :: set可能更有效率。 而且元素可能是不可交换的。
std :: set的内存开销也大得多,因为它必须在其节点之间存储许多指针。