拓扑排序 使用DAG思想推理任务执行顺序
有勇气的牛排
29
AI大模型
2025-12-10 21:26:59
1 前言
在多任务系统、工作流编排、Agent 执行链(如 OCR → 清洗 → 翻译)、数据处理流程中,都需要解决一个核心问题:
多个任务之间存在依赖关系,该按什么顺序执行?
这正是 拓扑排序(Topological Sort) 的应用场景。
2 题目
任务处理的依赖关系如下:
task_dag = {‘A’: [‘F’], ‘B’: [], ‘C’: [‘A’, ‘B’], ‘D’: [], ‘E’: [‘A’, ‘C’], ‘F’: [‘B’]}
请输出任务的执行顺序?输入为dict,输出为list
给定一组任务及其依赖关系:
task_dag = {
'A': ['F'],
'B': [],
'C': ['A', 'B'],
'D': [],
'E': ['A', 'C'],
'F': ['B']
}
含义说明:
A 需要在 F 完成之后才能执行
C 需要依赖 A 和 B
F 依赖 B
B、D 无依赖,最先可执行
E 需要 A 和 C
我们的目标是:
输出所有任务的可执行顺序,如 ['B', 'D', 'F', 'A', 'C', 'E']。
这个顺序唯一吗?
不是,D 和 B 先后顺序可变,但必须满足所有依赖关系。
3 解决思路:拓扑排序
拓扑排序用于在 DAG(Directed Acyclic Graph,有向无环图) 中,找出节点的合规执行顺序。
核心逻辑:
- 找出所有 没有依赖 的任务(deps == [])→ 可立即执行
- 执行这些任务,并从 DAG 中移除它们
- 从所有剩余任务中,删除对已执行任务的依赖
- 若还存在任务,重复步骤 1
- 若某一步找不到可执行任务,则说明图中存在循环依赖(无法执行)
4 完整代码
import copy
from typing import List, Dict
def task_execution_order(task_dag: Dict[str, List[str]]) -> List[str]:
dag = copy.deepcopy(task_dag)
print(dag)
run_order = []
print(task_dag)
while dag:
print("====== start ======")
ready = []
for task, dependencies in dag.items():
print("查空任务:", task, dependencies)
if not dependencies:
ready.append(task)
if not ready:
raise ValueError("存在循环依赖,无法完成拓扑排序")
for task in ready:
print(f"执行任务:{task}")
run_order.append(task)
dag.pop(task)
for deps in dag.values():
"""
deps 不是副本,是指向 dag[key] 的引用。
修改 deps 就是在修改 dag[key] 本身。
"""
for task in ready:
if task in deps:
deps.remove(task)
return run_order
if __name__ == '__main__':
task_dag = {
'A': ['F'],
'B': [],
'C': ['A', 'B'],
'D': [],
'E': ['A', 'C'],
'F': ['B']
}
run_order = task_execution_order(task_dag)
print(f"run_order: {run_order}")

5 总结:拓扑排序在 DAG 编排中的价值
拓扑排序是多任务系统中最重要的算法之一,广泛用于:
- 工作流引擎(Workflow)
- 多模型 Agent 执行链
- 编译器任务依赖
- 数据处理 Pipeline
- 微服务启动顺序
- 异步任务调度
它能确保:
- 每个任务都在依赖完成后才执行
- 不会陷入循环依赖
- 得到一个稳定可靠的执行顺序
<h2><a id="1__0"></a>1 前言</h2>
<p>在多任务系统、工作流编排、Agent 执行链(如 OCR → 清洗 → 翻译)、数据处理流程中,都需要解决一个核心问题:</p>
<p><strong>多个任务之间存在依赖关系,该按什么顺序执行?</strong></p>
<p>这正是 <strong>拓扑排序(Topological Sort)</strong> 的应用场景。</p>
<h2><a id="2__10"></a>2 题目</h2>
<p>任务处理的依赖关系如下:<br />
task_dag = {‘A’: [‘F’], ‘B’: [], ‘C’: [‘A’, ‘B’], ‘D’: [], ‘E’: [‘A’, ‘C’], ‘F’: [‘B’]}<br />
请输出任务的执行顺序?输入为dict,输出为list</p>
<p>给定一组任务及其依赖关系:</p>
<pre><div class="hljs"><code class="lang-python">task_dag = {
<span class="hljs-string">'A'</span>: [<span class="hljs-string">'F'</span>],
<span class="hljs-string">'B'</span>: [],
<span class="hljs-string">'C'</span>: [<span class="hljs-string">'A'</span>, <span class="hljs-string">'B'</span>],
<span class="hljs-string">'D'</span>: [],
<span class="hljs-string">'E'</span>: [<span class="hljs-string">'A'</span>, <span class="hljs-string">'C'</span>],
<span class="hljs-string">'F'</span>: [<span class="hljs-string">'B'</span>]
}
</code></div></pre>
<p>含义说明:</p>
<ul>
<li><code>A</code> 需要在 <code>F</code> 完成之后才能执行</li>
<li><code>C</code> 需要依赖 <code>A</code> 和 <code>B</code></li>
<li><code>F</code> 依赖 <code>B</code></li>
<li><code>B</code>、<code>D</code> 无依赖,最先可执行</li>
<li><code>E</code> 需要 <code>A</code> 和 <code>C</code></li>
</ul>
<p>我们的目标是:<br />
<strong>输出所有任务的可执行顺序,如 <code>['B', 'D', 'F', 'A', 'C', 'E']</code></strong>。</p>
<p>这个顺序唯一吗?<br />
不是,D 和 B 先后顺序可变,但必须满足所有依赖关系。</p>
<h2><a id="3__43"></a>3 解决思路:拓扑排序</h2>
<p>拓扑排序用于在 <strong>DAG(Directed Acyclic Graph,有向无环图)</strong> 中,找出节点的合规执行顺序。</p>
<p>核心逻辑:</p>
<ol>
<li>找出所有 <strong>没有依赖</strong> 的任务(deps == [])→ 可立即执行</li>
<li>执行这些任务,并从 DAG 中移除它们</li>
<li>从所有剩余任务中,删除对已执行任务的依赖</li>
<li>若还存在任务,重复步骤 1</li>
<li>若某一步找不到可执行任务,则说明图中存在循环依赖(无法执行)</li>
</ol>
<h2><a id="4__55"></a>4 完整代码</h2>
<pre><div class="hljs"><code class="lang-python"><span class="hljs-keyword">import</span> copy
<span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> <span class="hljs-type">List</span>, <span class="hljs-type">Dict</span>
<span class="hljs-keyword">def</span> <span class="hljs-title function_">task_execution_order</span>(<span class="hljs-params">task_dag: <span class="hljs-type">Dict</span>[<span class="hljs-built_in">str</span>, <span class="hljs-type">List</span>[<span class="hljs-built_in">str</span>]]</span>) -> <span class="hljs-type">List</span>[<span class="hljs-built_in">str</span>]:
<span class="hljs-comment"># 拷贝字典,防止修改原数据</span>
dag = copy.deepcopy(task_dag)
<span class="hljs-comment"># dag = {k: v[:] for k, v in task_dag.items()}</span>
<span class="hljs-built_in">print</span>(dag)
run_order = []
<span class="hljs-built_in">print</span>(task_dag)
<span class="hljs-keyword">while</span> dag:
<span class="hljs-built_in">print</span>(<span class="hljs-string">"====== start ======"</span>)
<span class="hljs-comment"># 找出依赖为空的任务</span>
<span class="hljs-comment"># ready = [task for task, deps in dag.items() if not deps]</span>
ready = []
<span class="hljs-keyword">for</span> task, dependencies <span class="hljs-keyword">in</span> dag.items():
<span class="hljs-built_in">print</span>(<span class="hljs-string">"查空任务:"</span>, task, dependencies)
<span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> dependencies: <span class="hljs-comment"># 空列表表示没有依赖</span>
ready.append(task)
<span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> ready:
<span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">"存在循环依赖,无法完成拓扑排序"</span>)
<span class="hljs-comment"># 执行任务</span>
<span class="hljs-keyword">for</span> task <span class="hljs-keyword">in</span> ready:
<span class="hljs-built_in">print</span>(<span class="hljs-string">f"执行任务:<span class="hljs-subst">{task}</span>"</span>)
run_order.append(task) <span class="hljs-comment"># 记录到顺序</span>
dag.pop(task) <span class="hljs-comment"># 删除已执行任务</span>
<span class="hljs-comment"># 删除已执行任务 在其他任务中的依赖</span>
<span class="hljs-keyword">for</span> deps <span class="hljs-keyword">in</span> dag.values():
<span class="hljs-string">"""
deps 不是副本,是指向 dag[key] 的引用。
修改 deps 就是在修改 dag[key] 本身。
"""</span>
<span class="hljs-keyword">for</span> task <span class="hljs-keyword">in</span> ready:
<span class="hljs-keyword">if</span> task <span class="hljs-keyword">in</span> deps:
deps.remove(task)
<span class="hljs-keyword">return</span> run_order
<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">'__main__'</span>:
task_dag = {
<span class="hljs-string">'A'</span>: [<span class="hljs-string">'F'</span>],
<span class="hljs-string">'B'</span>: [],
<span class="hljs-string">'C'</span>: [<span class="hljs-string">'A'</span>, <span class="hljs-string">'B'</span>],
<span class="hljs-string">'D'</span>: [],
<span class="hljs-string">'E'</span>: [<span class="hljs-string">'A'</span>, <span class="hljs-string">'C'</span>],
<span class="hljs-string">'F'</span>: [<span class="hljs-string">'B'</span>]
}
run_order = task_execution_order(task_dag)
<span class="hljs-built_in">print</span>(<span class="hljs-string">f"run_order: <span class="hljs-subst">{run_order}</span>"</span>)
</code></div></pre>
<p><img src="https://www.couragesteak.com/tcos/article/7822c54c78e5a82aef42c2b2ca049a69.png" alt="DAG任务处理" /></p>
<h2><a id="5__DAG__120"></a>5 总结:拓扑排序在 DAG 编排中的价值</h2>
<p>拓扑排序是多任务系统中最重要的算法之一,广泛用于:</p>
<ul>
<li>工作流引擎(Workflow)</li>
<li>多模型 Agent 执行链</li>
<li>编译器任务依赖</li>
<li>数据处理 Pipeline</li>
<li>微服务启动顺序</li>
<li>异步任务调度</li>
</ul>
<p>它能确保:</p>
<ul>
<li><strong>每个任务都在依赖完成后才执行</strong></li>
<li><strong>不会陷入循环依赖</strong></li>
<li><strong>得到一个稳定可靠的执行顺序</strong></li>
</ul>
评论区