优先级经常更改的作业优先级队列的数据结构

Data structure for a priority queue of jobs with priority that changes often

本文关键字:队列 数据结构 作业优先级 优先级 常更改      更新时间:2023-10-16

我有一个工人类,我可以向工作人员提交作业。worker 保留这些作业并按优先级顺序依次运行它们(优先级基本上可以是任何未签名的 int(。在这种情况下,std::p riority_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::p riority_queue 或一组似乎不太合适。普通的 std::list 似乎是一个更好的选择:优先级更改或更新/删除是 O(1(,当我需要删除作业时,O(N( 遍历整个列表以找到最高优先级的项目,这应该比修改优先级更频繁。

另一个观察结果是,即使工作优先级经常变化,这些变化并不一定会导致顺序变化,例如,我可以简单地更新我集合的关键元素(通过丢弃恒常性或使密钥可变?(,如果这种更改仍然会保留修改后的元素在左右节点之间。您对这样的优先级队列有什么建议?任何提升容器或自定义数据结构设计都是可以的。

在设置/映射的情况下,我使用优先级作为键。为了使键在我的情况下是唯一的,每个键实际上是两个整数:作业序列号(派生自我为每个新请求递增的原子整数(和实际优先级编号。这样,如果我添加多个具有相同优先级的作业,它们将按计划顺序执行,因为序列号将使它们保持有序。

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

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

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

如果存储指向堆中项目的指针,则还必须存储对堆中位置的反向引用。重新排序堆时,仅交换指针并更新反向引用。

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

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

以下是 IndexMinQueue 的 java 实现,希望对您有所帮助。索引最小队列