web-dev-qa-db-fra.com

Comment itérer sur une priority_queue?

Puis-je parcourir un priority_queue ou queue standard en c ++ avec un itérateur (comme un vector)? Je ne veux pas utiliser pop parce que ma file d'attente est supprimée. 

Merci pour toute aide

51
mina70

priority_queue n'autorise pas l'itération parmi tous les membres, probablement parce qu'il serait trop facile d'invalider l'ordre de priorité de la file d'attente (en modifiant les éléments que vous traversez) ou peut-être que ce n'est pas une justification.

La solution officielle consiste à utiliser un vector à la place et à gérer vous-même la priorité avec make_heap, Push_heap et pop_heap. Dans la réponse de @ Richard, une autre solution consiste à utiliser une classe dérivée de priority_queue et à accéder au stockage sous-jacent ayant une visibilité protected.

13
xan

Vous pouvez le faire comme ça - bam! Notez que les éléments ne sont pas nécessairement "triés" dans la file d'attente, du moins en ce qui concerne une itération simple du conteneur.

#include <queue>
#include <cstdlib>
#include <iostream>
using namespace std;

template <class T, class S, class C>
S& Container(priority_queue<T, S, C>& q) {
    struct HackedQueue : private priority_queue<T, S, C> {
        static S& Container(priority_queue<T, S, C>& q) {
            return q.*&HackedQueue::c;
        }
    };
    return HackedQueue::Container(q);
}

int main()
{
    priority_queue<int> pq;
    vector<int> &tasks = Container(pq);

    cout<<"Putting numbers into the queue"<<endl;
    for(int i=0;i<20;i++){
        int temp=Rand();
        cout<<temp<<endl;
        pq.Push(temp);
    }

    cout<<endl<<"Reading numbers in the queue"<<endl;
    for(vector<int>::iterator i=tasks.begin();i!=tasks.end();i++)
        cout<<*i<<endl;

    cout<<endl<<"Taking numbers out of the queue"<<endl;
    while(!pq.empty()){
        int temp=pq.top();
        pq.pop();
        cout<<temp<<endl;
    }

    return 0;
}
12
Richard

Un queue fournit délibérément une interface limitée, qui exclut l'itération. Mais comme queue utilise deque comme conteneur sous-jacent, pourquoi ne pas utiliser directement deque?

#include <iostream>
#include <queue>
using namespace std;

int main() {
  deque<int> q;
  q.Push_back(1);
  q.Push_back(2);
  q.Push_back(3);
  for(deque<int>::iterator it = q.begin(); it != q.end(); ++it)
    cout << *it << endl;
}

Réponse similaire pour une file d'attente prioritaire: non, vous ne pouvez pas. Dans ce cas cependant, une vector est utilisée par défaut. Dans les deux cas, vous pouvez accéder au conteneur sous-jacent pour effectuer une itération sur eux. Voir cette question pour une lecture plus approfondie.

11
marcog

Oui, faites une copie de priority_queue et parcourez-la.

4
Lie Ryan

Ce n'est pas possible. Vous devrez utiliser un conteneur différent, probablement un deque vous servirait mieux.

3
Björn Pollex

Les files d'attente sont totalement différentes des vecteurs et sont utilisées à des fins différentes. Les files d’attente prioritaires sont simplement triées deques sans accès direct à l’arrière. Cependant, si vous voulez désespérément faire cela pour quelque méthode que ce soit, ce que vous pouvez faire est de supprimer l’élément top/front, de l’ajouter à une liste/un tableau/un vecteur, puis de repousser l’élément dans votre file d’attente pendant 0; i <q.size (); i ++). J'ai suivi un cours sur les structures de données Java et c'était la réponse à une question d'examen. De plus, c'est la seule méthode à laquelle je peux penser.

2
sj755
#include <queue>
#include <iostream>

int main() {
    std::priority_queue<int> pq;

    pq.Push_back(1);
    pq.Push_back(2);
    pq.Push_back(3);

    std::priority_queue<int> temp = pq;

    while (!temp.empty()) {
        std::cout << temp.top() << std::endl;
        temp.pop();
    }

    return 0;
}
2
Snooze

J'ai trouvé cela après avoir trébuché sur votre question. Il existe un moyen très simple de le faire en écrivant une implémentation héritée de std :: priority_queue. C'est tout de 14 lignes.

http://www.linuxtopia.org/online_books/programming_books/c++_practical_programming/c++_practical_programming_189.html

1
Jonathan Henson

Priority_queue C++ n'offre pas de pointeur .begin () (comme le ferait vector), que vous pouvez utiliser pour le parcourir.

Si vous souhaitez parcourir la file d'attente prioritaire pour rechercher si elle contient une valeur, créez peut-être une file d'attente prioritaire pour le wrapper et utilisez un hachage défini pour garder une trace de ce que vous avez dans la file d'attente.

class MyPriorityQueue {

   MyPriorityQueue() {}

   void Push(int item) {
     if (!contains(item)){
       pq_.Push(item);
       set_.emplace(item);
     }
   }
   void pop() {
     if (!empty()) {
       int top = pq_.top();
       set_.erase(top);
       pq_.pop();
     }
   }
   int top() { return pq_.top(); }
   bool contains(int item) { return set_.find(item) != set_.end(); }
   bool empty() const { return set_.empty(); }

 private:
   std::priority_queue<int> pq_;
   std::unordered_set<int> set_;
};
0
mario

J'ai moi-même posé la même question. J'ai trouvé qu'il était très difficile, voire impossible, d'accéder à la structure de données sous-jacente à la file d'attente prioritaire. Dans mon cas, c'était un vecteur d'objets.

Cependant, j'ai fini par utiliser un tas de bibliothèque de modèles standard. C'est presque aussi facile qu'une file d'attente prioritaire (il faut deux instructions pour pousser et sauter, contre 1 pour le pq), sinon le comportement semble être identique Et Je peux obtenir la structure de données sous-jacente si Je ne le modifie pas.

0
David E.

Pour des raisons de base, un std::multiset vous donnera des propriétés similaires mais avec la possibilité d’itérer:

  • Les éléments triés et personnalisés Less peuvent être définis
  • Les clés peuvent se produire plusieurs fois
  • Rapide d'accès et de suppression du premier élément
0
AndiDog

Beaucoup de ces réponses reposent sur le codage/l'utilisation de nombreuses fonctionnalités arcanes de C++. Ça va, c'est amusant et on finance des programmeurs coûteux. Une solution directe rapide, peu coûteuse à programmer mais plus coûteuse à exécuter est la suivante:

// 
// Only use this routine when developing code, NOT for production use!!
//
// Note. _pq is in private for a class that uses the priority queue
// and listQueue is a public method in that same class.
//
void listQueue() {

    // allocate pointer to a NEW container
    priority_queue<int>* new_pq = new  priority_queue<int>;

    while (!_pq->empty()) {

        int el = _pq->top();

        cout << "(" << el << ")" << endl;

        new_pq->Push(el);

        _pq->pop();

    } // end while;

    // remove container storage
    delete(_pq);

    // viola, new container same as the old
    _pq = new_pq;

} // end of listQueue;

En passant, il semble parfaitement déconseillé de fournir un itérateur pour une priorité_queue, en particulier lorsqu'il s'agit d'une classe de conteneur pour une structure ou.

0
JackCColeman