我正在创建一个简单的游戏,我使用std::priority_queue
将命令提供给小队(每个小队都有priority_queue<command>
)。
每20秒,机器人会分析情况并向priority_queue
发送命令。
如何设置priority_queue
固定大小,例如,将大小设置为10?期望的效果是,当达到最大值时,如果我向队列添加2个新命令,则会自动删除2个具有最低优先级的现有命令。
答案 0 :(得分:8)
将它包装在另一个将为您执行此操作的类中。标准本身不提供此类功能。
答案 1 :(得分:5)
这是偷偷摸摸的,但你应该能够覆盖std::priority_queue
的功能来做你需要的事情。这似乎适用于我所做的一些测试:
template<typename T>
class fixed_priority_queue : public std::priority_queue<T>
{
public:
fixed_priority_queue(unsigned int size) : fixed_size(size) {}
void push(const T& x)
{
// If we've reached capacity, find the FIRST smallest object and replace
// it if 'x' is larger
if(this->size() == fixed_size)
{
// 'c' is the container used by priority_queue and is a protected member.
auto beg = c.begin(); auto end = c.end();
auto min = std::min_element(beg, end);
if(x > *min)
{
*min = x;
// Re-make the heap, since we may have just invalidated it.
std::make_heap(beg, end);
}
}
// Otherwise just push the new item.
else
{
priority_queue::push(x);
}
}
private:
fixed_priority_queue() {} // Construct with size only.
const unsigned int fixed_size;
// Prevent heap allocation
void * operator new (size_t);
void * operator new[] (size_t);
void operator delete (void *);
void operator delete[] (void*);
};
这里发生了什么?
std::priority_queue
类priority_queue::push()
方法,与新项目交换最低项目使用:
const unsigned int LIMIT = 20;
fixed_priority_queue<int> fooQueue(LIMIT);
// Testing.
for(int i=0; i<40; i++)
fooQueue.push(rand());
for(int i=0; i<LIMIT; i++)
{
printf("%i\n", fooQueue.top());
fooQueue.pop();
}
这里有什么不好的?
priority_queue
在底层容器上调用make_heap
(默认情况下为std :: vector)。我不确定通常调用的频率是多少,但如果队列已满,我们会经常调用它。我认为它可以在priority_queue::push()
内调用吗?希望这很有用,如果不是至少有趣的话。
答案 2 :(得分:2)
Aryabhatta's answer of another question适用于此问题。
您使用最大堆。
假设您有一个N元素堆(实现为数组) 包含到目前为止看到的N个最小元素。
当元素进入时,检查最大值(O(1)时间),和 拒绝,如果它更大。
之前几条评论中提到的迭代是不必要的。
答案 3 :(得分:1)
一个想法是创建一个最小优先级队列,并使用size()方法仅将优先级队列填充到所需级别。像这样:
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
struct Compare {
// priority queue is max heap by default, use compare
// to make it minheap
bool operator() (const int& a, const int& b) {
return a>b;
}
};
typedef priority_queue<int, vector<int>, Compare> pqSize;
void priorityQueueFixedSize(pqSize& pq, int size, vector<int>& vec) {
for (int i=0;i<vec.size();i++) {
if (pq.size() < size) {
pq.push(vec[i]);
} else {
if (vec[i] > pq.top()) {
pq.pop();
pq.push(vec[i]);
}
}
}
}
void printPQ(pqSize& pq) {
while (!pq.empty()) {
cout << pq.top() << " ";
pq.pop();
}
cout << endl;
}
int main() {
vector<int> vec(20,0);
for (int i=0;i<vec.size();i++) {
vec[i] = i;
}
pqSize pq;
priorityQueueFixedSize(pq,10, vec);
printPQ(pq);
}
这样,优先级队列中最多只能保留10个元素。
答案 4 :(得分:0)
该标准提供了一组原始堆操作:
make_heap
sort_heap
push_heap
pop_heap
我们可以将其包装在一个简单的类中,该类具有一个Command[10]
或'std :: array
我个人建议不要将其包装在函数中,因为您可能希望采用ECS模式并在命令上应用更多可能的操作,而不仅仅是作为优先级队列。我们知道,如果您使用优先级队列,则可能会有不必要的副本或移动。
答案 5 :(得分:0)
一个有点棘手的解决方案是不使用 std::priotity_queue,而是使用大小为 N+1 的 std::array,其中只有前 N 个元素是真正的队列。而不是用最低优先级替换元素,而是用新命令覆盖 queue.back()
。比 pop_queue(q.begin(), q.end())
向队列添加新命令。
这是我的代码,有效:
int main() {
std::array<int, 10> queue;
queue.fill(500);
for(int i = 0; i < 1000; i++)
{
queue.back() = rand() % 1000;
pop_heap(queue.begin(), queue.end(), std::greater<int>());
//print_queue(queue);
assert(is_heap(queue.begin(), queue.end()-1, std::greater<int>()));
}
}
另一件事是,您的规范是否是您真正想要的。不要忘记优先考虑年龄。如果你不这样做,那么在一百万次迭代之后,你将有 10 个元素,具有最高优先级。它们何时被添加到队列中无关紧要。