Diferencia entre std::set y std::priority_queue

7 minutos de lectura

avatar de usuario
penélope

Ya que ambos std::priority_queue y std::set (y std::multiset) son contenedores de datos que almacenan elementos y le permiten acceder a ellos de manera ordenada, y tienen la misma complejidad de inserción O(log n)¿cuáles son las ventajas de usar uno sobre el otro (o, qué tipo de situaciones requieren uno u otro?)?

Si bien sé que las estructuras subyacentes son diferentes, no estoy tan interesado en la diferencia en su implementación como en la comparación de sus actuación y idoneidad para varios usos.

Nota: Sé acerca de los no-duplicados en un conjunto. Por eso también mencioné std::multiset ya que tiene exactamente el mismo comportamiento que el std::set pero se puede usar cuando los datos almacenados se pueden comparar como elementos iguales. Así que, por favor, no comentes sobre el problema de una o varias teclas.

  • La cola de prioridad solo ofrece acceso a la más grande elemento, mientras que el conjunto le da un orden completo de todos elementos. Esta interfaz más débil significa que las implementaciones pueden ser más eficientes (por ejemplo, puede almacenar los datos reales de la cola en un vectorque puede tener un mejor rendimiento debido a su localidad de memoria).

    – KerrekSB

    13 de abril de 2012 a las 13:39

  • @KerrekSB La respuesta más elaborada es en realidad un comentario: D Nadie ha comentado sobre el rendimiento en absoluto. ¿Podría poner eso en una respuesta, tal vez expandirse un poco?

    – Penélope

    13 de abril de 2012 a las 13:41

  • El punto clave de la biblioteca estándar es que priority_queue se implementa en términos de heap*-algoritmos de <algorithm>aplicado a un contenedor de acceso aleatorio subyacente.

    – KerrekSB

    13 de abril de 2012 a las 13:58

  • @KerrekSB sería genial si pusieras todo eso en una respuesta

    – Penélope

    13 de abril de 2012 a las 14:24

avatar de usuario
ataúd de jerry

Una cola prioritaria solamente te da acceso a una elemento en orden ordenado, es decir, puede obtener el elemento de mayor prioridad y, cuando lo elimine, puede obtener la siguiente prioridad más alta, y así sucesivamente. Una cola de prioridad también permite elementos duplicados, por lo que se parece más a un conjunto múltiple que a un conjunto. [Edit: As @Tadeusz Kopec pointed out, building a heap is also linear on the number of items in the heap, where building a set is O(N log N) unless it’s being built from a sequence that’s already ordered (in which case it is also linear).]

Un conjunto le permite acceso completo en orden ordenado, por lo que puede, por ejemplo, encontrar dos elementos en algún lugar en el medio del conjunto y luego atravesar en orden de uno a otro.

  • Otra diferencia es que construir una cola de prioridad a partir de un conjunto dado de valores solo tiene una complejidad lineal.

    – Tadeusz Kopec por Ucrania

    13 de abril de 2012 a las 13:43

  • En cuanto al rendimiento, encontré que el multiconjunto funciona mejor que una cola de prioridad al simular el comportamiento de un caso de uso que tenemos. En nuestra aplicación del mundo real, cualquiera funcionará bien, pero las características más ricas de un conjunto son importantes, por lo que en general lo convierten en un ganador. YMMV, pero sospecho que un conjunto múltiple es la mejor opción en la mayoría de los casos.

    – Nick

    20 junio 2015 a las 21:40


  • @TadeuszKopec Usando emplace_hint y insert con el iterador de sugerencias, también se puede lograr una complejidad lineal para la entrada ordenada.

    – Tomilov Anatoliy

    16 mayo 2016 a las 19:01


avatar de usuario
Ixanezis

std::priority_queue permite hacer lo siguiente:

  1. Insertar un elemento O(log n)
  2. Consigue el pequeñísimo elemento O(1)
  3. borrar el pequeñísimo elemento O(log n)

tiempo std::set tiene más posibilidades:

  1. Insertar cualquier elemento O(log n) y la constante es mayor que en std::priority_queue
  2. Encontrar ningún elemento O(log n)
  3. Encuentre un elemento, >= que el que está buscando O(log n) (lower_bound)
  4. Borrar ningún elemento O(log n)
  5. Borrar ningún elemento por su iterator O(1)
  6. Mover al elemento anterior/siguiente en orden ordenado O(1)
  7. Consigue el pequeñísimo elemento O(1)
  8. Consigue el más grande elemento O(1)

  • O tal vez moverse al elemento anterior/siguiente en orden ordenado funcione en O(log n) – no me conozco 🙁

    – Ixanezis

    13 de abril de 2012 a las 14:08

  • Para conjunto, obtenga el elemento más pequeño y el más grande, en caso de que sea O(1) u O(log n). Esta respuesta contradice la respuesta de Andrew Tomazos. ¿Cual es correcta?

    – Roberto Wang

    5 sep 2016 a las 20:29

  • Para el conjunto, escoger el elemento más pequeño es efectivamente un *s.begin()y el elemento más grande es un *s.rbegin()así que como ambas funciones tienen una complejidad constante, creo O(1) es correcto. es.cppreference.com/w/cpp/container/set/begin

    – Ixanezis

    9 de septiembre de 2016 a las 11:19

  • Si el conjunto se construye como un BST, ¿cómo podríamos encontrar el comienzo () en el tiempo O (1)? pregunta similar va a rbegin(). A menos que tengamos dos espacios O(1) adicionales para rastrear los valores máximo y mínimo (no estoy seguro si este es el caso en STL).

    – Robert Wang

    10 de septiembre de 2016 a las 0:38


  • @RobertWang Este es el caso en STL. Asi que O(1) es la respuesta correcta.

    – Tomilov Anatoliy

    21 de febrero de 2018 a las 19:18

avatar de usuario
Andrés Tomazos

set/multiset generalmente están respaldados por un árbol binario. http://en.wikipedia.org/wiki/Binary_tree

Priority_queue generalmente está respaldado por un montón. http://en.wikipedia.org/wiki/Heap_(estructura_de_datos)

Entonces, la pregunta es realmente ¿cuándo debería usar un árbol binario en lugar de un montón?

Ambas estructuras están dispuestas en un árbol, sin embargo, las reglas sobre la relación entre los antepasados ​​son diferentes.

Llamaremos a las posiciones P para padre, L para hijo izquierdo y R para hijo derecho.

En un árbol binario L

En un montón P

Entonces, los árboles binarios se clasifican “hacia los lados” y los montones se clasifican “hacia arriba”.

Entonces, si vemos esto como un triángulo, en el árbol binario L, P, R están completamente ordenados, mientras que en el montón se desconoce la relación entre L y R (solo su relación con P).

Esto tiene los siguientes efectos:

  • Si tiene una matriz desordenada y quiere convertirla en un árbol binario, se necesita O(nlogn) tiempo. Si quieres convertirlo en un montón, solo necesitas O(n) tiempo, (ya que solo se compara para encontrar el elemento extremo)

  • Los montones son más eficientes si solo necesita el elemento extremo (el más bajo o el más alto según alguna función de comparación). Los montones solo hacen las comparaciones (perezosamente) necesarias para determinar el elemento extremo.

  • Los árboles binarios realizan las comparaciones necesarias para ordenar la colección completa y mantener la colección completa ordenada todo el tiempo.

  • Los montones tienen una búsqueda en tiempo constante (vistazo) del elemento más bajo, los árboles binarios tienen una búsqueda en tiempo logarítmico del elemento más bajo.

  • Esto no está elaborado en absoluto. Lo que pedí fueron diferentes situaciones en las que preferirías usar una sobre la otra.

    – Penélope

    13 de abril de 2012 a las 13:37

  • Simplemente publicar una respuesta a medio escribir para ser el primero en responder una Q no es realmente bueno en mi opinión.

    – Penélope

    13 de abril de 2012 a las 13:45

  • @penelope: Pensé que una respuesta corta inmediata sería más útil mientras tanto que esperar una respuesta larga.

    – Andrés Tomazos

    13 de abril de 2012 a las 13:46

  • ¿Puede dar más detalles sobre cómo convertir una matriz no ordenada en un montón en O(n) cuando usas priority_queue. De acuerdo a esto: cplusplus.com/reference/algorithm/push_heap push_heap es O(log(n))por lo que empujar elementos a una cola de prioridad uno por uno parecería tomar O(n log(n)). Quizás si construyes todo el montón a la vez, es más rápido. ¿Es posible construir un priority_queue de un conjunto desordenado en O(n) ¿tiempo?

    – Cheshirekow

    23 de junio de 2013 a las 15:10

  • Mirar el elemento más bajo es O (1) para multiset (es decir, *begin() )

    – PlantillaRex

    17 oct 2016 a las 11:14

  • Muchísimas gracias. Estás respondido a mi pregunta.

    – Robotex

    21 oct 2019 a las 21:39

¿Ha sido útil esta solución?