在计算机科学领域，有向图的**拓扑排序**（英語：）或**拓撲定序**（英語：）是对其顶点的一种线性排序，使得对于从顶点{\displaystyle u}到顶点{\displaystyle v}的每个有向边{\displaystyle uv}，{\displaystyle u}在排序中都在{\displaystyle v}之前。

例如，图形的顶点可以表示要执行的任务，并且边可以表示一个任务必须在另一个任务之前执行的约束；在这个应用中，拓扑排序只是一个有效的任务顺序。

当且仅当图中没有定向环时（即有向无环图），才有可能进行拓扑排序。

任何有向无环图至少有一个拓扑排序。已知有算法可以在线性时间内，构建任何有向无环图的拓扑排序。

在图论中，由一个有向无环图的顶点组成的序列，当且仅当满足下列条件时，才能称为该图的一个拓扑排序：

1. 序列中包含每个顶点，且每个顶点只出现一次；
2. 若A在序列中排在B的前面，则在图中不存在从B到A的路径。

算法
--

### 卡恩算法

卡恩于1962年提出了该算法。简单来说，假设L是存放结果的列表，先找到那些入度为零的节点，把这些节点放到L中，因为这些节点没有任何的父节点。然后把与这些节点相连的边从图中去掉，再寻找图中的入度为零的节点。对于新找到的这些入度为零的节点来说，他们的父节点已经都在L中了，所以也可以放入L。重复上述操作，直到找不到入度为零的节点。如果此时L中的元素个数和节点总数相同，说明排序完成；如果L中的元素个数和节点总数不同，说明原图中存在环，无法进行拓扑排序。

```
L ← 包含已排序的元素的列表，目前为空
S ← 入度为零的节点的集合
当 S 非空时：
    将节点n从S移走
    将n加到L尾部
    选出任意起点为n的边e = (n,m)，移除e。如m没有其它入边，则将m加入S。
    重复上一步。
如图中有剩余的边则：
    return error   *(图中至少有一个环)*
否则： 
    return L   *(L为图的拓扑排序)*
```

  

### 深度优先搜索

另一种拓扑排序的方法运用了深度优先搜索。深度优先搜索以任意顺序循环遍历图中的每个节点。若搜索进行中碰到之前已经遇到的节点，或碰到叶节点，则中止算法。

  

```
L ← 一个空的 用来存放已排序的节点的列表
当图中存在未永久标记的节点时：
    选出任何未永久标记的节点n
    visit(n)
```

```
function visit(节点 n)
    如n被永久标记：
        return
    如n被临时标记：
        stop   (不是定向无环图，至少有一个环)
  
    将n临时标记
  
    对于每一个以n为起点的边(n,m)：
        visit(m)
  
    去掉n的临时标记
    将n永久标记
    在L的起始位置插入n（如L已有内容 后移它们以空出起始位置）
```

例子
--

在某校的选课系统中，存在这样的规则：每门课可能有若干门先修课，如果要修读某一门课，则必须要先修读此课程所要求的先修课后才能修读。假设一个学生同时只能修读一门课程，那么，被选课系统允许的他修完他需要所有课程的顺序是一个拓扑序。

在这个例子中，每一门课程对应有向图中的一个顶点，每一个先修关系对应一条有向边（从先修课指向需要先修课的课）。

参考资料
----

外部链接
----

* NIST Dictionary of Algorithms and Data Structures: topological sort （页面存档备份，存于）
* 埃里克·韦斯坦因.. MathWorld.

|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 

| * 
* 论
* 编
排序算法 |
| --- |
|  |
| 理论 | 
* 計算複雜性理論
* 大O符号
* 全序关系
* 数据结构术语列表
* 原地算法
* 稳定性
* 比较排序
* 自适应排序（英语：）
* 排序网络（英语：）
* 整数排序（英语：）
* X+Y排序（英语：）
* 量子排序（英语：）

 |
|  |
| 交换排序 | 
* 冒泡排序
* 鸡尾酒排序
* 奇偶排序
* 梳排序
* 侏儒排序
* 快速排序
* 慢速排序
* 臭皮匠排序
* Bogo排序

 |
|  |
| 选择排序 | 
* 选择排序
* 堆排序
* 平滑排序（英语：）
* 笛卡尔树排序（英语：）
* 锦标赛排序（英语：）
* 圈排序（英语：）
* 弱堆排序（英语：）

 |
|  |
| 插入排序 | 
* 插入排序
* 希尔排序
* 伸展排序
* 二叉查找树排序
* 图书馆排序
* 耐心排序

 |
|  |
| 归并排序 | 
* 归并排序
* 梯级归并排序（英语：）
* 振荡归并排序（英语：）
* 多相归并排序（英语：）

 |
|  |
| 分布排序 | 
* 美国旗帜排序（英语：）
* 珠排序
* 桶排序
* 爆炸排序（英语：）
* 计数排序
* 比較計數排序
* 插值排序
* 鸽巢排序
* 相邻图排序（英语：）
* 基数排序
* 闪电排序（英语：）

 |
|  |
| 并发排序 | 
* 双调排序器（英语：）
* Batcher归并网络
* 两两排序网络（英语：）

 |
|  |
| 混合排序 | 
* 塊排序（英语：）
* Tim排序
* 内省排序
* Spread排序（英语：）
* 归并插入排序（英语：）

 |
|  |
| 其他 | 
* 拓撲排序
* 煎餅排序
* 意粉排序（英语：）

 |

 |

|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 

| * 
* 论
* 编
算法 |
| --- |
|  |
| 排序 | 

|  |  |
| --- | --- |
| 比较排序 | 
* 冒泡排序
* 选择排序
* 插入排序
* 希尔排序
* 快速排序
* 归并排序
* 堆排序
* 鸡尾酒排序
* 梳排序
* 侏儒排序
* 图书馆排序
* 内省排序
* 奇偶排序

 |
|  |
| 线性时间排序 | 
* 鸽巢排序
* 基数排序
* 計數排序
* 桶排序

 |
|  |
| 并行排序 | 
* 排序网络（英语：）
* Batcher归并网络

 |
|  |
| 不实用的 | 
* Bogo排序
* 臭皮匠排序

 |
|  |
| 图 | 
* 拓撲排序

 |

 |
|  |
| 搜索 | 

|  |  |
| --- | --- |
| 列表 | 
* 线性搜索
* 二分搜索
* 插值搜尋

 |
|  |
| 树・图 | 
* 广度优先搜索
	+ 最良優先搜索（英语：）
	+ 均一开销搜索
	+ A\*
* 深度优先搜索
	+ 迭代深化深度优先搜索
	+ 深度限制搜索（日语：）
	+ 双向搜索
* 分枝限定法（英语：）

 |
|  |
| 字符串 | 
* KMP算法
* 博耶-穆尔字符串搜索算法
* AC自动机算法
* 拉宾-卡普算法
* bitap算法

 |

 |
|  |
| 最短路问题 | 
* 戴克斯特拉算法
* 贝尔曼-福特算法
* A\*搜尋演算法
* Floyd-Warshall算法

 |
|  |
| 最小生成树 | 
* 普林姆算法
* 克鲁斯克尔演算法

 |
|  |
| 最大流最小割 | 
* 福特-富尔克森算法
* 埃德蒙兹-卡普算法
* 迪尼茨算法

 |
|  |
| 线性规划 | 
* 单纯形法
* 卡马卡尔算法（英语：）

 |
|  |
| 順序統計量 | 
* 选择算法
* 中位数的中位数（英语：）

 |
|  |
| 種類 | 
* 精确算法
* 近似算法
* 随机化算法

 |
|  |
| 其他 | 
* 分治法
* 动态规划
* 贪心算法

 |
|  |
| **Category:算法** |

 |