需求中有一个“杀手”功能,它阻止了简单的解决方案。约束的方向不是一个,而是两个:
- 任务 A 必须在任务 B 之后执行 => A“依赖”于 B
- 任务 A 必须在任务 B => B “依赖” A 之前执行
在现实世界的情况下,我面临的这些依赖关系更加复杂,但这并不重要:所有都可以分解为刚才描述的模拟情况。
现在算法:
步骤 1:将每个任务的所有这些约束编译为每个任务的一组单向依赖项。然后可以很容易地处理单向依赖关系。首先构建图,然后执行循环检测,然后执行拓扑排序(感谢 Dimitry 一词)的第一个想法可以完全放弃。所以每个项目最终都有一组依赖项:
- 如果任务 A 必须在任务 B 之后执行,我们将“B”存储在 A 的依赖集中。
- 如果任务 A 必须在任务 B 之前执行,我们将“A”存储在 B 的依赖集中。
在这样做的同时,我们甚至可以对这些依赖项进行完整性检查。如果约束规范中有问题,我们可以在此步骤中轻松检测到这一点。
第 2 步:现在我们有一个非常简单的问题,因为只有一种方式的依赖关系。这些可以被认为是先决条件:只有满足所有先决条件,才能执行任务。现在我们可以进行如下操作:
将所有任务打包到一个名为 /notYetProcessed/ 的列表中
创建空列表 /result/
虽然 /notYetProcessed/ 中仍有要处理的任务,但请执行以下操作:
创建空列表/remaining/
对于 /t/ in /notYetProcessed/ 中的所有任务,请执行以下操作:
如果 /t/ 满足所有依赖项,则执行以下操作:
将 /t/ 添加到 /result/
否则:
将 /t/ 添加到 /remaining/
如果 /length(notYetProcessed)/ 匹配 /length(remaining)/ 然后执行:
以错误终止
/notYetProcessed/ = /remaining/
在外部 while 条件终止后,result
将包含要处理的任务列表,其顺序遵循开头定义的所有约束。
如果上述算法因错误而终止,这意味着: * 在此循环中无法处理任何任务 * 这意味着:无法解决某些依赖关系 * 这意味着:存在一个任务依赖循环,完全涉及剩余的任务
第3步:现在将存储在中的所有任务一一处理result
,您会没事的。
正如您所看到的,这甚至可以在不构建数据的特殊图形表示的情况下完成。通过接受我们可以掌握的第一个解决方案(=所有可能排序顺序的第一个变体),对数据“直接”执行拓扑排序。
可能有一些更有效的算法来解决这个问题(在执行第一次依赖项编译之后)我可能不知道。如果是这样,我会很高兴了解他们!