J'ai une application (C++) qui je pense serait bien servie par une STL priority_queue
. La documentation dit:
Priority_queue est un adaptateur de conteneur, ce qui signifie qu'il est implémenté au-dessus d'un type de conteneur sous-jacent. Par défaut, ce type sous-jacent est un vecteur, mais un type différent peut être sélectionné explicitement.
et
Les files d'attente prioritaires sont un concept standard et peuvent être implémentées de différentes manières; cette implémentation utilise des tas.
J'avais précédemment supposé que top()
est O(1)
, et que Push()
serait un O(logn)
(les deux raisons pour lesquelles je a choisi le priority_queue
en premier lieu) - mais la documentation ne confirme ni ne nie cette hypothèse.
En creusant plus profondément, les documents du concept Sequence disent:
La complexité de l'insertion et de l'effacement d'un seul élément dépend de la séquence.
Le priority_queue
Utilise un vector
(par défaut) comme tas, ce qui:
... prend en charge l'accès aléatoire aux éléments, l'insertion et la suppression d'éléments à temps constant à la fin, et l'insertion et la suppression linéaires d'éléments au début ou au milieu.
Je déduis que, en utilisant le priority_queue
Par défaut, top()
est O(1)
et Push()
est O(n)
.
Question 1: Est-ce correct? (top()
l'accès est O(1)
et Push()
est O(n)
?)
Question 2: Pourrais-je atteindre l'efficacité O(logn)
sur Push()
si j'utilisais un set
(ou multiset
) au lieu d'un vector
pour l'implémentation du priority_queue
? Quelles seraient les conséquences de faire cela? Quelles autres opérations en souffriraient en conséquence?
N.B .: Je m'inquiète de l'efficacité du temps ici, pas de l'espace.
L'adaptateur de file d'attente prioritaire utilise les algorithmes de tas de bibliothèque standard pour créer et accéder à la file d'attente - c'est la complexité de ces algorithmes que vous devriez rechercher dans la documentation.
L'opération top () est évidemment O(1) mais vraisemblablement vous voulez pop () le tas après l'avoir appelé qui (selon Josuttis ) est O (2 * log (N)) et Push () est O(log(N)) - même source.
Et à partir de la norme C++, 25.6.3.1, Push_heap:
Complexité: tout au plus comparaisons log (dernier - premier).
et pop_heap:
Complexité: au plus 2 * log (dernières - premières) comparaisons.
top()
- O(1) - car il retourne juste l'élément @ front.
Push()
-
Push_into_heap - Au plus, des comparaisons log (n). O (connecté)
donc la complexité de Push () est - log (n)
Non, ce n'est pas correct. top () est O(1) et Push () est O (log n). Lisez la note 2 de la documentation pour voir que cet adaptateur ne permet pas d'itérer à travers le vecteur. Neil est correct à propos de pop (), mais cela permet toujours de travailler avec le tas en faisant des insertions et des suppressions en temps O (log n).
Si la structure de données sous-jacente est un tas, top () sera à temps constant et Push (EDIT: et pop) sera logarithmique (comme vous le dites). Le vecteur est juste utilisé pour stocker ces choses comme ceci:
TAS:
1
2 3
8 12 11 9
VECTEUR (utilisé pour stocker)
1 2 3 8 12 11 9
Vous pouvez le considérer comme l'élément à la position des enfants de i est (2i) et (2i + 1)
Ils utilisent le vecteur car il stocke les données de manière séquentielle (ce qui est beaucoup plus efficace et compatible avec le cache que discret)
Quelle que soit la façon dont il est stocké, un tas doit toujours être implémenté (en particulier par les dieux qui ont créé la bibliothèque STD) afin que la pop soit constante et que Push soit logarithmique.
La structure de données sous-jacente Priority_queue C++ STL est la structure de données Heap (Heap est un ADT non linéaire qui, basé sur un arbre binaire complet et un arbre binaire complet, est implémenté via un conteneur vectoriel (ou Array).
ex Input data : 5 9 3 10 12 4.
Heap (Considering Min heap) would be :
[3]
[9] [4]
[10] [12] [5]
NOW , we store this min heap in to vector,
[3][9][4][10][12][5].
Using formula ,
Parent : ceiling of n-1/2
Left Child : 2n+1
Right Child : 2n+2 .
Hence ,
Time Complexity for
Top = O(1) , get element from root node.
POP()= O(logn) , During deletion of root node ,there is chance to violation of heap order . hence restructure of heap order takes at most O(logn) time (an element might move down to height of tree).
Push()= O(logn) , During insertion also , there might chance to violation of heap order . hence restructure of heap order takes at most O(logn) time (an element might move up to root from leaf node).
Q1: Je n'ai pas regardé dans la norme, mais AFAIK, en utilisant vector
(ou deque
btw), la complexité serait O(1)
pour top()
, O(log n)
pour Push()
et pop()
. J'ai comparé une fois std::priority_queue
Avec mon propre tas avec O(1)
Push()
et top()
et O(log n)
pop()
et ce n'était certainement pas aussi lent que O(n)
.
Q2: set
n'est pas utilisable comme conteneur sous-jacent pour priority_queue
(Pas une séquence), mais il serait possible d'utiliser set pour implémenter une file d'attente prioritaire avec O(log n)
Push()
et pop()
. Cependant, cela ne surpasserait probablement pas l'implémentation de std::priority_queue
Sur std::vector
.