拓扑排序的典型应用
- 修课顺序
- 项目编译顺序
拓扑排序的求解方法
对于一个有向无环图来说拓扑排序的结果遵循如下规则,即如果有一条从顶点
v
i
v_i
vi指向顶点
v
j
v_j
vj的边,那么最后的排序结果中
v
i
v_i
vi一定在
v
j
v_j
vj 的前面。例如
这个图的拓扑排序就是1 2 3 4 5
。
同一个图可以有多个拓扑排序结果,对于上面那个图,还有一种排序结果就是1 2 3 5 4
。
为了可以进行拓扑排序,给定的图中不可以有环。我们可以简单论证一下,假设图中的顶点为 v 1 , v 2 , v 3 . . . v n v_1,v_2,v_3...v_n v1,v2,v3...vn ,并且这些点构成环,也就是说有一条边从 v i v_i vi指向 v i + 1 v_{i+1} vi+1(其中 1 ≤ i < n 1≤i<n 1≤i<n)并且有一条边从 v n v_{n} vn指向 v 1 v_1 v1。那么现在我们进行拓扑排序的话, v n v_n vn一定要排在 v 1 v_1 v1之前,因为有一条 v n v_n vn指向 v 1 v_1 v1的边。而 v i v_{i} vi一定排在 v i + 1 v_{i+1} vi+1的前面,因为有一条从 v i v_{i} vi指向 v i + 1 v_{i+1} vi+1的边。那么这意味着 v 1 v_1 v1排在 v n v_n vn前面,所以这和前面矛盾。
接着我们思考一下如何找到一个图的拓扑排序。最基本的思路就是找到一组顶点的排列,使得所有指向
v
j
v_j
vj的顶点
v
i
v_i
vi,都排在顶点
v
j
v_j
vj的前面。我们维护一个数组T
存储拓扑排序结果。
因此,对于一个拥有N
个顶点的图来说,我们先建立一个大小为N
的数组
i
n
_
d
e
g
r
e
e
[
]
in\_degree[]
in_degree[],其
i
t
h
i^{th}
ith元素表示
v
i
v_i
vi的入度(也就是所有指向
v
i
v_i
vi结点的数量)。我们将顶点
v
i
v_i
vi插入到T
中的同时,要将
i
n
_
d
e
g
r
e
e
[
v
j
]
in\_degree[v_j]
in_degree[vj]减1
(其中
v
j
v_j
vj表示
v
i
v_i
vi指向的所有点)。在任何时候我们都只能插入
i
n
_
d
e
g
r
e
e
[
]
in\_degree[]
in_degree[]为0
的顶点。
BFS
代码
from collections import defaultdict
def topSort(N, pre):
"""
N 为 顶点数量
pre: List[List[int]] 为关系数组
"""
res, in_degree, vis, queue = [], [0]*N, [False]*N, []
graph = defaultdict(list)
for i, j in pre:
graph[i].append(j)
in_degree[j] += 1
for i in range(N):
if in_degree[i] == 0:
queue.append(i)
vis[i] = True
while queue:
cur = queue.pop(0)
N -= 1
res.append(cur)
for i in graph[cur]:
in_degree[i] -= 1
if not vis[i] and in_degree[i] == 0:
queue.append(i)
vis[i] = True
return res if N == 0 else []
我们举一个例子看看这个算法是如何工作的,考虑下面这个图
一开始
i
n
_
d
e
g
r
e
e
[
0
]
=
0
in\_degree[0]=0
in_degree[0]=0并且
T
T
T是空
所以我们从
q
u
e
u
e
queue
queue中删除0,将其添加到
T
T
T中。接着我们将所有0指向的点(1,2,3)
的入度减1
。现在
i
n
_
d
e
q
u
e
[
1
]
=
0
in\_deque[1]=0
in_deque[1]=0,我们将1
添加到
q
u
e
u
e
queue
queue中
接着我们将1从
q
u
e
u
e
queue
queue中弹出,然后添加到T
中。接着我们将所有1指向的点(2,4)
的入度减1
。现在
i
n
_
d
e
q
u
e
[
2
]
=
0
in\_deque[2]=0
in_deque[2]=0,我们将2添加到
q
u
e
u
e
queue
queue中
接着做类似操作
所以我们最终得到的拓扑排序数组是0,1,2,3,4,5
。
- 我们也可以通过
DFS
来处理该问题,使用DFS
的话,我们就不用建立 i n _ d e g r e e in\_degree in_degree数组了。 - 下面是
DFS
的代码
from collections import defaultdict
def DFS_topSort(N, pre):
res, vis = [], [0]*N
graph = defaultdict(list)
for i, j in pre:
graph[i].append(j) # graph[i]的值为一个列表,存储着 指向i的所有顶点
def dfs(node):
if vis[node] == 1:
return False
elif vis[node] == -1:
return True
vis[node] = 1
for i in graph[node]:
if not dfs(i):
return False
res.insert(0, node)
vis[node] = -1
return True
for i in range(N):
if vis[i] == 0 and not dfs(i):
return []
return res
N = 3
relations = [[0,2],[1,2]]
print(DFS_topSort(N, relations))
reference
https://blog.youkuaiyun.com/qq_17550379/article/details/97610885
topological-sort