优先级经常变化的作业优先级队列的数据结构

时间:2018-06-12 04:41:59

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

我有一个工人阶级,我可以向工人提交工作。 Worker保留这些作业并按优先级顺序依次运行它们(优先级基本上可以是任何unsigned int)。对于这种情况,std :: priority_queue或甚至std :: set / map可用于存储按优先级排序的作业,然后worker可以在O(1)中按顺序提取它们。添加作业将是O(log N)。

现在,我的要求是能够更改任何提交作业的优先级。在std :: set / map的情况下,我需要删除并添加具有不同优先级的作业。这将是O(log N),并且在set / map的基础上,它将在内部重新分配节点afaik(尽管可能使用C ++ 17可能会避免这种情况)。让我不寻常的是,在我的情况下,我会比安排或执行它们更频繁地更新工作优先级。基本上我可能会安排一次工作,在执行之前我可能最终会更新其优先级数千次。事实上,每项工作的优先级将改变为每秒10-20次。 在我的情况下,假设队列中不会有超过10K的作业,这是相当安全的。在我的流程开始时,我希望它总是增长到10K左右的工作,并且随着这些工作被移除,队列最终应该几乎是空的,偶尔会增加10-50个新工作,但它不应该增长超过1000个工作岗位。工作将以每秒几个工作的速度被删除。由于我的奇怪要求,频繁的优先级更新std :: priority_queue或一组似乎不太合适。普通std :: list似乎是一个更好的选择:优先级更改或更新/删除是O(1),当我需要删除作业时,它是O(N)遍历整个列表以找到应该更少发生的最高优先级项目而不是修改优先事项。

另一个观察结果是,即使工作优先级经常发生变化,这些变化也不一定会导致订单变更,例如:我可以简单地更新我的集合的关键元素(通过抛弃constness或使密钥可变?),如果该更改仍然保留左右节点之间的修改元素。您对此类优先级队列有何建议?任何增强容器或自定义数据结构设计都可以。

在set / map的情况下,我使用priority作为键。为了使密钥在我的情况下唯一,每个密钥实际上是两个整数:作业序列号(从我为每个新请求递增的原子int派生)和实际优先级编号。这样,如果我添加具有相同优先级的多个作业,它们将按照它们的预定顺序执行,因为序列号会使它们保持有序。

2 个答案:

答案 0 :(得分:1)

基本上您正在寻找IndexPriorityQueue。您可以根据需要实现自己的索引优先级队列变量。

索引优先级队列允许您减少键或增加键,即基本上您可以增加和减少作业的优先级。

以下是IndexMinQueue的java实现,希望对您有所帮助。 IndexMinQueue

答案 1 :(得分:1)

简单的优先级堆应该符合您的要求。插入,删除和优先级更改都是O(log n)。但是你说通常优先级的改变不会导致订单发生变化。因此,在更改优先级时优先级堆的情况下,您将针对父级和2个子级检查更改的项,如果没有违反任何堆条件,则不需要向上或向下堆操作。因此,很少需要完整的O(log n)时间。实际上它更像是O(1)。

现在为了有效运作,至关重要的是,给定一个项目我可以在O(1)中找到该项目在堆中的位置并访问父项和子项。

如果堆只包含数组中的项,那么这只是指针算术。缺点是重新排序堆意味着复制项目。

如果存储指向堆中项目的指针,那么您还必须在它们自己的项目中存储堆中位置的后向引用。当您重新排序堆时,您只需交换指针并更新后引用。