Étant donné que _std::priority_queue
_ et _std::set
_ (et _std::multiset
_) sont des conteneurs de données qui stockent des éléments et vous permettent d'y accéder de manière ordonnée, et ont la même complexité d'insertion O(log n)
, quels sont les avantages d'utiliser l'un par rapport à l'autre (ou, quel genre de situations appellent l'un ou l'autre?)?
Bien que je sache que les structures sous-jacentes sont différentes, je ne suis pas autant intéressé par la différence dans leur mise en œuvre que par la comparaison de leurs performances et adéquation pour diverses utilisations.
Note: Je connais les non-doublons dans un ensemble. C'est pourquoi j'ai également mentionné _std::multiset
_ car il a exactement le même comportement que le _std::set
_ mais peut être utilisé là où les données stockées sont autorisées à comparer en tant qu'éléments égaux. Alors s'il vous plaît, ne commentez pas le problème des clés simples/multiples.
Une file d'attente prioritaire niquement vous donne accès à l'élément n dans l'ordre trié - c'est-à-dire que vous pouvez obtenir l'élément de priorité la plus élevée, et lorsque vous le supprimez, vous pouvez obtenir le suivant priorité la plus élevée, etc. Une file d'attente prioritaire permet également de dupliquer des éléments, c'est donc plus comme un multiset qu'un ensemble. [Edit: Comme l'a souligné @Tadeusz Kopec, la construction d'un tas est également linéaire sur le nombre d'éléments dans le tas, où la construction d'un ensemble est O (N log N) sauf s'il est construit à partir d'une séquence qui est déjà ordonnée (dans ce cas il est également linéaire).]
Un ensemble vous permet un accès complet dans l'ordre trié, vous pouvez donc, par exemple, trouver deux éléments quelque part au milieu de l'ensemble, puis parcourir dans l'ordre de l'un à l'autre.
std::priority_queue
Permet de faire ce qui suit:
O(log n)
O(1)
O(log n)
tandis que std::set
a plus de possibilités:
O(log n)
et la constante est supérieure à celle de std::priority_queue
O(log n)
O(log n)
(lower_bound
)O(log n)
O(1)
O(1)
O(1)
set/multiset sont généralement soutenus par un arbre binaire. http://en.wikipedia.org/wiki/Binary_tree
priority_queue est généralement soutenu par un tas. http://en.wikipedia.org/wiki/Heap_ (data_structure)
La question est donc vraiment de savoir quand utiliser un arbre binaire au lieu d'un tas?
Les deux structures sont disposées dans un arbre, mais les règles concernant la relation entre les ancêtres sont différentes.
Nous appellerons les positions P pour parent, L pour enfant gauche et R pour enfant droit.
Dans un arbre binaire L <P <R.
Dans un tas P <L et P <R
Les arbres binaires trient donc "latéralement" et les tas trient "vers le haut".
Donc, si nous regardons cela comme un triangle, dans l'arbre binaire L, P, R sont complètement triés, alors que dans le tas, la relation entre L et R est inconnue (seule leur relation avec P).
Cela a les effets suivants:
Si vous avez un tableau non trié et que vous souhaitez le transformer en arbre binaire, cela prend O(nlogn)
temps. Si vous voulez le transformer en tas, cela ne prend que O(n)
temps, (car il compare juste pour trouver l'élément extrême)
Les tas sont plus efficaces si vous n'avez besoin que de l'élément extrême (le plus bas ou le plus élevé selon une fonction de comparaison). Les tas ne font que les comparaisons (paresseusement) nécessaires pour déterminer l'élément extrême.
Les arbres binaires effectuent les comparaisons nécessaires pour ordonner la collection entière et garder la collection entière triée tout le temps.
Les tas ont une recherche en temps constant (aperçu) de l'élément le plus bas, les arbres binaires ont une recherche en temps logarithmique de l'élément le plus bas.
Étant donné que _
std::priority_queue
_ et _std::set
_ (et _std::multiset
_) sont des conteneurs de données qui stockent des éléments et vous permettent d'y accéder de manière ordonnée, et ont la même complexité d'insertionO(log n)
, quels sont les avantages d'utiliser l'un par rapport à l'autre (ou, quel genre de situations appellent l'un ou l'autre?)?
Même si insérer et effacer les opérations pour les deux conteneurs ont la même complexité O (log n), ces opérations pour _std::set
_ sont plus lentes que pour _std::priority_queue
_. En effet, _std::set
_ effectue de nombreuses allocations de mémoire. Chaque élément de _std::set
_ est stocké à sa propre allocation. _std::priority_queue
_ (avec le conteneur _std::vector
_ sous-jacent par défaut) utilise une allocation unique pour stocker tous les éléments. D'autre part, _std::priority_queue
_ utilise de nombreuses opérations d'échange sur ses éléments tandis que _std::set
_ utilise uniquement l'échange de pointeurs. Donc, si l'échange est une opération très lente pour le type d'élément, l'utilisation de _std::set
_ peut être plus efficace. De plus, l'élément peut ne pas être échangeable du tout.
La surcharge de mémoire pour _std::set
_ est beaucoup plus importante, car elle doit stocker de nombreux pointeurs entre ses nœuds.