C++ 如何告诉std::priority_队列刷新其顺序?
我有一个指向C++ 如何告诉std::priority_队列刷新其顺序?,c++,stl,priority-queue,C++,Stl,Priority Queue,我有一个指向struct city的指针优先级队列。我在优先级队列之外修改了这些指针指向的对象,并希望告诉优先级队列根据新值对自身进行“重新排序” 我该怎么办 例如: #include <iostream> #include <queue> using namespace std; struct city { int data; city *previous; }; struct Compare { bool operator() ( cit
struct city
的指针优先级队列。我在优先级队列之外修改了这些指针指向的对象,并希望告诉优先级队列根据新值对自身进行“重新排序”
我该怎么办
例如:
#include <iostream>
#include <queue>
using namespace std;
struct city {
int data;
city *previous;
};
struct Compare {
bool operator() ( city *lhs, city *rhs )
{
return ( ( lhs -> data ) >= ( rhs -> data ) );
}
};
typedef priority_queue< city *, vector< city * >, Compare > pqueue;
int main()
{
pqueue cities;
city *city1 = new city;
city1 -> data = 5;
city1 -> previous = NULL;
cities.push( city1 );
city *city2 = new city;
city2 -> data = 3;
city2 -> previous = NULL;
cities.push( city2 );
city1 -> data = 2;
// Now how do I tell my priority_queue to reorder itself so that city1 is at the top now?
cout << ( cities.top() -> data ) << "\n";
// 3 is printed :(
return 0;
}
#包括
#包括
使用名称空间std;
结构城市{
int数据;
城市*以前;
};
结构比较{
布尔运算符()(城市*lhs,城市*rhs)
{
返回((左侧->数据)>=(右侧->数据));
}
};
typedef priority_queue,Compare>pqueue;
int main()
{
城市;
城市*城市1=新城;
城市1->数据=5;
city1->previous=NULL;
城市。推动(城市1);
城市*城市2=新城;
城市2->数据=3;
city2->previous=NULL;
城市。推送(城市2);
城市1->数据=2;
//现在,我如何告诉我的优先级队列重新排序,以使city1现在处于顶部?
cout data)基于此,似乎没有办法做到这一点,无需清空和重新插入
如果您愿意离开优先级队列(但仍然需要堆)如果你需要保存一个有序的集合,你可以考虑以下的解决方案:使用<代码> STD::设置< /Cord>,并更新值,删除该项,更新其值并将其放回集合中。这将给你O(log n)。更新一个项目的复杂性,这是在通常的堆结构中所能达到的最佳状态(假设您可以通过sift-up-sift-down过程访问其内部以进行批量处理)。
std::set
唯一的缺点是初始化一个包含n个项目的集合的时间。(O(n log n)而不是O(n))。这有点老套,但并不违法,它完成了任务
std::make_heap(const_cast<city**>(&cities.top()),
const_cast<city**>(&cities.top()) + cities.size(),
Compare());
std::make_heap(const_cast(&cities.top()),
const_cast(&cities.top())+cities.size(),
比较());
更新:
如果出现以下情况,请不要使用此黑客:
- 基础容器不是
vector
Compare
functor的行为会导致外部副本的顺序与存储在priority\u队列中的Compare
副本的顺序不同
- 您不完全理解这些警告的含义
你可以编写自己的容器适配器来包装堆算法。priority\u queue
不过是一个简单的包装器,围绕着make/push/pop\u heap
这是一个老问题,但当我想自己做这件事时,我对任何答案都不完全满意。不需要任何破解。std::previorCity_队列
包含所有合法且惯用的机制
std::priority\u queue
有两个非常有用的数据成员,c
(底层容器)和comp
(比较谓词)
同样有用的是,标准要求容器
模板类型必须是SequenceContainer
的模型,谁的迭代器是随机访问迭代器
的模型
这很有用,因为std::make_heap
的Iter
参数类型具有相同的RandomAccessIterator
模型要求
这是一种冗长的说法,std::priority_queue
是堆的包装器,因此std::make_heap(std::begin(c),std::end(c),comp)
必须是有效的表达式
“坏”消息是c
和comp
受到保护。这实际上是个好消息,原因有两个:
您不能意外地破坏堆
如果从std::priority\u queue
派生,则可以有意修改堆
因此,诀窍是从std::priority_queue
派生优先级队列,在成员函数中,以任意方式对内部堆c
进行变异,然后调用std::make_heap(std::begin(c),std::end(c),comp);
将其转换回有效堆
一般来说,从STL容器继承不是一个坏主意吗
嗯,是的,但是
对于年轻人和/或粗心的人来说,这可能是个坏主意,原因有两个:缺少多态性析构函数和切片的风险
多态析构函数
实际上,通过指向std容器基类的指针拥有std容器并没有合理的用例。容器是轻量级的(当容器中没有任何内容时)而且可以便宜地移动。您可以考虑使用案例,但我可以保证,通过将容器封装在另一个堆分配对象中,无论您打算做什么,都可以做得更好。在设计良好的代码中,这一点不应该成为一个问题。在任何情况下,从优先级队列
模板类私下继承消除了这种风险
切片
当然,当我们传递继承的对象时,存在切片的风险。这里的答案是从priority\u队列
基类中私下继承,然后在派生类中使用using
,只导出我们希望共享的基类接口部分
下面的示例已更新以显示这一点
下面是一个真实项目的示例
要求:
保留必须通知客户端更改的主题队列。按此主题最早收到通知的时间戳对队列进行排序。不允许重复的主题名称
下面是一个工作演示:
#include <queue>
#include <string>
#include <chrono>
#include <cassert>
#include <iostream>
using topic_type = std::string;
using timestamp_clock = std::chrono::system_clock;
using timestamp_type = timestamp_clock::time_point;
struct notification {
topic_type topic;
timestamp_type timestamp;
};
bool topic_equals(const notification& l, const topic_type& r) {
return l.topic == r;
}
bool topic_equals(const topic_type& l, const notification& r) {
return l == r.topic;
}
bool age_after(const notification& l , const notification& r) {
return l.timestamp > r.timestamp;
}
bool age_after(const notification& l , const timestamp_type& r) {
return l.timestamp > r;
}
bool age_after(const timestamp_type& l , const notification& r) {
return l > r.timestamp;
}
struct greater_age
{
template<class T, class U>
bool operator()(const T& l, const U& r) const {
return age_after(l, r);
}
};
template<class T>
struct pending_queue_traits;
template<>
struct pending_queue_traits<notification>
{
using container_type = std::vector<notification>;
using predicate_type = greater_age;
using type = std::priority_queue<notification, container_type, predicate_type>;
};
class pending_notification_queue
: private pending_queue_traits<notification>::type
{
using traits_type = pending_queue_traits<notification>;
using base_class = traits_type::type;
public:
// export the constructor
using base_class::base_class;
// and any other members our clients will need
using base_class::top;
using base_class::pop;
using base_class::size;
bool conditional_add(topic_type topic, timestamp_type timestamp = timestamp_clock::now())
{
auto same_topic = [&topic](auto& x) { return topic_equals(topic, x); };
auto i = std::find_if(std::begin(c), std::end(c), same_topic);
if (i == std::end(c)) {
this->push(notification{std::move(topic), std::move(timestamp)});
return true;
}
else {
if (timestamp < i->timestamp) {
i->timestamp = std::move(timestamp);
reorder();
return true;
}
}
return false;
}
private:
void reorder() {
std::make_heap(std::begin(c), std::end(c), comp);
}
};
// attempt to steal only the base class...
void try_to_slice(pending_queue_traits<notification>::type naughty_slice)
{
// danger lurks here
}
int main()
{
using namespace std::literals;
auto pn = pending_notification_queue();
auto now = timestamp_clock::now();
pn.conditional_add("bar.baz", now);
pn.conditional_add("foo.bar", now + 5ms);
pn.conditional_add("foo.bar", now + 10ms);
pn.conditional_add("foo.bar", now - 10ms);
// assert that there are only 2 notifications
assert(pn.size() == 2);
assert(pn.top().topic == "foo.bar");
pn.pop();
assert(pn.top().topic == "bar.baz");
pn.pop();
// try to slice the container. these expressions won't compile.
// try_to_slice(pn);
// try_to_slice(std::move(pn));
}
#包括
#包括
#包括
#包括
#包括
使用topic_type=std::string;
使用时间戳时钟=标准::时钟::系统时钟;
使用时间戳类型=时间戳时钟::时间点;
结构通知{
话题型话题;
时间戳\类型时间戳;
};
bool topic_等于(常量通知和l、c
better_priority_queue::updatable_priority_queue<int,int> pQ;
pQ.push(0, 30); // or pQ.set(0, 30)
pQ.push(1, 20);
pQ.push(2, 10);
pQ.update(2, 25); // or pQ.set(2, 25)
while(!pQ.empty())
std::cout << pQ.pop_value().key << ' ';
// Outputs: 0 2 1