【问题标题】:How to iterate over a priority_queue?如何迭代priority_queue?
【发布时间】:2011-05-27 22:52:00
【问题描述】:

我可以在 c++ 中使用迭代器(如 vector)遍历标准 priority_queue 或标准 queue 吗?我不想使用 pop,因为它会导致我的队列出队。

感谢您的帮助

【问题讨论】:

    标签: c++ stl queue


    【解决方案1】:

    priority_queue 不允许遍历所有成员,大概是因为它太容易使队列的优先级顺序无效(通过修改您遍历的元素)或者这可能是“不是我的工作”的理由。

    官方的解决方法是改用vector,并使用make_heap、push_heap 和pop_heap 自己管理优先级。在@Richard 的回答中,另一种解决方法是使用从priority_queue 派生的类并访问具有protected 可见性的底层存储。

    【讨论】:

    • 这应该在文档中,让我们知道priority_queue的用处是多么有限......叹息。
    【解决方案2】:

    你可以这样做 - 砰!请注意,队列中的项目不一定按“排序”顺序排列,至少在容器的直接迭代方面是这样。

    #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;
    }
    

    【讨论】:

    • 子类化 std:: 容器是危险的/错误的,因为它们缺少虚拟析构函数。
    • 所以这个例子确实存在内存泄漏,因为子类化std:: containers 是危险的/错误的,因为它们缺少虚拟析构函数?
    • 我看不出这怎么会泄露。我们将 HackedQueue 定义为容器的子类,并添加一个静态方法。然后我们使用静态方法来提取受保护的参数。我们甚至没有实例化这个子类的一个对象...
    • 什么是“.*&”?使用不同类成员的指针调用 q 的成员?
    【解决方案3】:

    queue 特意提供了一个有限的接口,不包括迭代。但是既然queue使用deque作为底层容器,为什么不直接使用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;
    }
    

    优先级队列的类似答案:不,你不能。但在这种情况下,默认使用vector。在这两种情况下,您都不能访问底层容器来迭代它们。如需进一步阅读,请参阅this question。

    【讨论】:

    • 我想我会回答你的问题“为什么不直接使用双端队列?”在我的场景中,我想在不影响实现的情况下记录 priority_queue 的内容(通过更改类型)。这是一项日志记录工作,性能并不重要,因此制作副本可以正常工作。
    • 您能否将答案中的“否”部分提升到顶部。坦率地说,我不明白为什么 deque,它与问题无关。
    【解决方案4】:
    #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;
    }
    

    【讨论】:

    • @PeterdeRivaz 不知道,我想我脑子里放了个屁。删除我的评论。
    • std::priority_queue 没有 push_back 方法。它有一个push 方法。
    【解决方案5】:

    是的,制作一个 priority_queue 的副本并对其进行迭代。

    【讨论】:

    • 如果性能不是问题,这听起来是一个合理的想法。如果您提供代码 sn-p,您将获得我的支持。
    【解决方案6】:

    这是不可能的。您将不得不使用不同的容器,deque 可能会为您提供最好的服务。

    【讨论】:

    • 大多数事情都是可能的,但有些事情是不可取的。
    【解决方案7】:

    我在偶然发现您的问题后发现了这一点。通过编写继承自 std::priority_queue 的实现,有一种非常简单的方法。一共14行。

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

    【讨论】:

      【解决方案8】:

      队列与向量完全不同,用于不同的目的。优先队列是简单的排序双端队列,不能直接访问后面。但是,如果您非常想对任何方法执行此操作,您可以做的是弹出顶部/前端元素,将其添加到列表/数组/向量中,然后将元素推回队列中 for(size_t i = 0; i

      【讨论】:

        【解决方案9】:

        其中许多答案都依赖于编码/使用许多 C++ 神秘特性。没关系,有趣并资助昂贵的程序员。一个快速、编程成本低但运行成本更高的直接解决方案是:

        // 
        // 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;
        

        顺便说一句,不为 priority_queue 提供迭代器似乎完全不明智,尤其是当它是 a or 结构的容器类时。

        【讨论】:

          【解决方案10】:

          C++ priority_queue 不提供可用于迭代它的 .begin() 指针(就像 vector 一样)。

          如果您想遍历优先级队列以搜索它是否包含值,则可以创建一个包装器优先级队列并使用哈希集来跟踪队列中的内容。

          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_;
          };
          

          【讨论】:

            【解决方案11】:

            出于基本目的,std::multiset 将为您提供类似的属性,但具有迭代能力:

            • 排序的项目,可以定义自定义Less
            • 键可以出现多次
            • 快速访问和删除第一项

            【讨论】:

            • 只是一个提示:std::priority_queue 默认情况下首先按最大元素排序,而首先按最低元素排序 - 所以可能必须颠倒排序。 std::set 也可以在元素不同时使用。
            【解决方案12】:

            How I can find value in priority queue? 几乎一样。容器受保护,使用此技巧访问它。

            c++ 版本 >=11

            #include<iostream>
            #include<queue>
            using namespace std;
            template<class T, class C = vector<T>, class P = less<typename C::value_type> >
            struct heapq :std::priority_queue<T,C,P> {
                using priority_queue<T,C,P>::priority_queue;
                typename C::iterator begin() { return std::priority_queue<T, C, P>::c.begin(); }
                typename C::iterator end() { return std::priority_queue<T, C, P>::c.end(); }
            };
            int main(){
                heapq<int> q;
                q.push(100);
                q.push(80);
                q.push(60);
                for(auto e:q) cout<<e<<endl;
            }
            

            【讨论】:

              【解决方案13】:

              我自己也有同样的问题。我发现要访问优先级队列下的数据结构非常困难,也许是不可能的。在我的例子中,这是一个对象向量。

              但是,我最终使用了标准模板库堆。它几乎和优先级队列一样简单(推送和弹出需要两条指令,而 pq 需要 1 条指令),否则行为似乎相同 和 如果我不修改它,我可以得到底层数据结构。

              【讨论】:

              • 刚刚看到回答队列中我上面的人给出了相同的答案,而且做得更好!
              【解决方案14】:

              如果您想以复杂的有序方式推送项目 (logN)。但也想以递增的顺序迭代元素,您可以使用set&lt;int&gt;。 集合通常实现为二叉搜索树。

              集合是可迭代的(begin、end、rbegin、rend 等)

              【讨论】:

                【解决方案15】:

                我遇到了同样的问题,我想迭代优先级队列而不出队(因此破坏了我的队列)。我通过将我的priority_queue 指针重新转换为指向向量的指针来使它对我有用(因为我的priority_queue 使用向量作为它的容器)。这是它的样子:

                class PriorityQueue {
                  private:
                    class Element {
                    int x; 
                    //Other fields
                    ...
                    ..
                    //Comparator function
                    bool operator()(const Element* e1, const Element* e2) const {
                        // Smallest deadline value has highest priority.
                        return e1->x > e2->x;
                    }
                    };
                    // Lock to make sure no other thread/function is modifying the queue
                    // Ofcourse we can do our operation w/o lock. if we are sure what is happening in other functions
                    pthread_mutex_t lock;   
                    std::priority_queue<Element*, std::vector<Element*>, Element> pq;
                
                  public:
                    PriorityQueue();
                    ~PriorityQueue();
                    //print the all the elements of the queue
                    void print_queue_elements() {
                        std::vector<PriorityQueue::Element*> *queue_vector;
                        //Acquire the lock
                        pthread_mutex_lock(&lock);
                        //recast the priority queue to vector
                        queue_vector = reinterpret_cast<std::vector<PriorityQueue::Element*> *>(&pq);
                        for(std::vector<PriorityQueue::Element*>::iterator it = (*queue_vector).begin(); it != (*queue_vector).end(); it++) {
                            //Assuming Element class has string function
                            printf("My Element %s", (*it)->string);
                            //other processing with queue elements
                        }
                        //Release the lock
                        pthread_mutex_unlock(&lock);    
                
                    }
                    //other functions possibly modifying the priority queue
                    ...
                    ..
                    .
                 };
                

                现在,由于我使用的是 reinterpret_cast,人们可以争论类型安全。但在这种情况下,我确信所有其他访问/更改队列的函数(所有这些都是安全的)。我觉得这比将整个队列的内容复制到其他容器要好得多。

                我实际上期待static_cast 工作.. 因为priority_queue 是容器上的适配器(在我们的例子中是向量),但它没有,我不得不使用reinterpret_cast。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-01-27
                  • 1970-01-01
                  • 2018-08-06
                  • 2016-04-09
                  • 1970-01-01
                  • 2021-06-22
                  • 2011-12-29
                  相关资源
                  最近更新 更多