【问题标题】:C++ Priority Queue - Reorder based on updated prioritiesC++ 优先级队列 - 根据更新的优先级重新排序
【发布时间】:2023-10-16 02:23:01
【问题描述】:

一些背景知识:我正在构建一个C++ 线程管理器,它允许用户创建一个AsyncJob 对象并分配执行优先级。我有一个 JobManager 单例类,它管理这些 AsyncJobs 的优先级队列,并在可用时将它们分配给线程。

问题:用户需要能够修改AFTER 创建的优先级。例如,基于一些运行时事件,文件可能需要比其他文件更紧急地加载。我面临的问题是优先级队列仅在调用push()pop() 时重新排序内部堆上的元素。据我所知,没有公开的界面允许人们根据不断变化的优先级请求重新排序。

我想做的是这样的:

  • 在我的JobManager 类中创建一个hashmap,该类保存指向优先级队列中对象的指针
  • 用户可以通过key访问请求的job,并通过hash map更新优先级
  • JobManager 然后向优先级队列发出信号,告知优先级已更改
  • 优先级队列在内部重新排序

解决此问题的最佳方法是什么?我应该期望创建自己的自定义优先级队列类吗?或者可能从std::priority_queue 扩展?

谢谢!

【问题讨论】:

  • 这些更改的频率如何?优先级可以上升和下降,或者只是上升,还是只是下降?我怀疑std::priority_queue 会做你想做的事,但如果我对你的设置有更多了解,我可以尝试为你指出正确的方向。
  • 变化很可能会上升;但是上下都是有效的。优先级更新不会那么频繁。
  • 根据*.com/q/649640/56778,您对std::priority_queue 不走运。您必须自己实现或找到一个可以做到的。
  • 请参阅“如何在 STL priority_queue 中进行有效的优先级更新?” *.com/questions/649640/…

标签: c++ multithreading asynchronous data-structures priority-queue


【解决方案1】:

一种选择可能是让您的 AsyncJob 处于“已取消”状态,并且在取消旧的优先级后,每次修改优先级时只需将新的(副本)AsyncJob 添加到您的 PQ 中。

与许多事情一样,我建议“最佳方式”是高度主观的,因为这取决于您的限制程度以及您如何量化“最佳”。我个人喜欢避免从标准库类型派生并尽可能地重新发明容器结构。

【讨论】:

  • 对于不经常更新的情况,这似乎是个好主意。
  • 有时简单的解决方案是最好的。我不得不提醒自己过早的优化是万恶之源。
【解决方案2】:

binary search tree 似乎是一个不错的选择。

二叉搜索树应该按优先级排序,其次,如果优先级不是唯一的,对象中的唯一标识符/标识符集。

如果您在想要更改之前不知道之前的优先级,则需要一种方法来查找它 - 可能是对象优先级的 hash table

这允许您在 O(log n) 中删除和重新插入特定项目,而不会失去 O(log n) 在其他操作上的性能(并且 O(1) 获得第一个)。


对于 C++:

如果优先级是唯一的,对象优先级的map 可以工作,否则set<pair> 其中第一个元素是优先级,第二个是对象(是的,我知道,这些不一定是 BST)。

对于哈希表,您可以使用 unordered_map 的对象优先级,或者简单地使用 map pre-C++11。

【讨论】:

    【解决方案3】:

    删除更改其优先级的项目然后添加它(或副本,如果新的优先级在逻辑上意味着新项目)可能是最简单的方法。毕竟,您不想从头开始对整个队列进行重新排序,只需对单个更改的项目进行重新排序 - 将其删除并插入即可。

    编辑:但是,正如 Paul Renton 所提到的,std::priority_queue 不允许在没有弹出头部的情况下获取单个项目,所以我会在 std::priority_queue 的启发下使用 std::vector 滚动我自己的优先级队列,它使用std::vector 默认情况下。我会公开我需要的东西,而不是std::priority_queue 的整个 API 集。我允许删除和插入按优先级正确排序的项目,而不是重新排序整个列表。

    【讨论】:

    • 问题是我无法通过迭代访问优先级队列中的元素。我相信从 pqueue 获取元素的唯一方法是调用 top()
    • 啊,对,当然 - 你只能通过弹出来从std::priority_queue 中取出项目......然后我会在std::priority_queue 的启发下使用std::vector 滚动我自己的优先级队列,默认使用std::vector。我将公开我需要的内容,而不是std::priority_queue 的整个 API 集。
    • @PaulRenton:编辑我的答案以反映您的评论。