簡體   English   中英

如何迭代priority_queue?

[英]How to iterate over a priority_queue?

我可以使用迭代器(如vector )遍歷 c++ 中的標准priority_queue或標准queue嗎? 我不想使用 pop 因為它會導致我的隊列出隊。

謝謝你的幫助

priority_queue不允許迭代所有成員,大概是因為使隊列的優先級排序無效(通過修改您遍歷的元素)太容易了,或者這可能是“不是我的工作”的理由。

官方的解決方法是改用vector並使用make_heappush_heappop_heap自己管理優先級。 在@Richard 的回答中,另一個解決方法是使用派生自priority_queue的類並訪問具有protected可見性的底層存儲。

你可以這樣做 - 砰! 請注意,項目在隊列中時不一定按“排序”順序排列,至少在容器的直接迭代方面是這樣。

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

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 在這兩種情況下,您都不能訪問底層容器來迭代它們。 請參閱此問題以進一步閱讀。

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

是的,復制 priority_queue 並迭代它。

我在偶然發現你的問題后發現了這一點。 通過編寫一個繼承自 std::priority_queue 的實現,有一種非常簡單的方法可以做到這一點。 全是14行。

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

這是不可能的。 您將不得不使用不同的容器,可能deque最適合您。

隊列與向量完全不同,用於不同的目的。 優先級隊列只是排序的雙端隊列,不能直接訪問后面。 但是,如果您非常想為任何方法執行此操作,您可以做的是彈出頂部/前端元素,將其添加到列表/數組/向量中,然后將元素推回您的隊列 for(size_t i = 0; i < q.size(); i++)。 我參加了 Java 數據結構課程,這是一個考試問題的答案。 另外,這是我能想到的唯一方法。

其中許多答案依賴於編碼/使用許多 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 提供迭代器似乎是完全不明智的,尤其是當它是一個或結構的容器類時。

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

出於基本目的, std::multiset將為您提供類似的屬性,但具有迭代能力:

  • 項目排序,自定義Less可以定義
  • 鍵可以出現多次
  • 快速訪問和刪除第一項

如何在優先級隊列中找到值? 差不多一樣。 容器受到保護,使用這個技巧來訪問它。

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

我自己也有同樣的問題。 我發現獲取優先級隊列底層的數據結構非常困難,甚至是不可能的。 就我而言,這是一個對象向量。

但是,我最終使用了標准模板庫堆。 它幾乎和優先級隊列一樣簡單(推送和彈出需要兩條指令,而 pq 需要 1 條指令),否則行為似乎是相同的,如果我不修改它,我可以獲得底層數據結構.

如果您想以具有復雜性(logN)的有序方式推送項目。 但是想要以遞增的順序迭代元素,您可以使用set<int> 集合通常被實現為二叉搜索樹。

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

制作副本並對其進行迭代 - 由@lie-ryan 建議

#include <iostream>
#include <queue>

using namespace std;
void make_copy(priority_queue<int, vector<int>> pq, vector<int> &arr)
{
    arr = {}; // if it was not empty , make it :)
    while (!pq.empty())
    {
        arr.push_back(pq.top());
        pq.pop();
    }
}
int main()
{
    priority_queue<int, vector<int>> q;
    q.push(1);
    q.push(2);
    q.push(3);
    vector<int> arr;
    make_copy(q, arr); // this will copy all elements of q to arr :)
    for (auto &x : arr)
    {
        cout << x << " ";
    }
}

我遇到了同樣的問題,我想在不出隊的情況下迭代優先級隊列(因此​​破壞了我的隊列)。 我通過將我的priority_queue 指針重新轉換為指向向量的指針(因為我的priority_queue使用vector 作為它的容器)使它對我有用。 這是它的樣子:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM