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