- 前置课程问题(课程表)
- AOV网络
- 拓扑排序
- 广度优先搜索(BFS)
- 一些课程的学习要求有先修课程,有些则不需要,没有先后顺序的课程就可以并行学习(进行);
-
在有向图中,有向边<Vi, Vj>表示活动Vi先于活动Vj进行,这种以顶点表示活动的有向图叫做AOV网络(Activity On Vertices)。 AOV中不能出现某项活动以自身作为先决条件的有向环(死循环)。
-
拓扑排序是把构成AOV网络的各个顶点排列成一个线性有序序列(按照前驱后继关系),他的一般过程:
(1) 按照给定关系构造顶点的邻接表;
(2) 设置数组in_degrees记录各顶点入度(前驱顶点个数),设置队列Q组织所有入度为0的顶点(无前驱顶点,这些顶点无先后关系,可以并行执行);
(3) 从0度队列弹出顶点(删0入度点),逐步遍历与它连接的端点,并将这些端点的入度减一(断边); 如果端点的入度减为0,则将端点压入0度队列;
重复上述步骤,直到:
全部顶点输出,拓扑有序序列形成;
图中有未输出的点,但跳出了循环,说明AOV网络存在有向环。
-
拓扑排序的实质是广度优先搜索,它是一个按照与零入度顶点相连的顶点逐一去搜索的。广度优先搜索是一个逐层遍历的过程,它将满足条件的初始顶点放入一个队列,然后逐个弹出(删点,FIFO),然后遍历所有与该顶点相连的端点(删边),队列中的每一个顶点就是一个层次的开端。
课程表问题的一个可行序列(python描述):
flat = lambda L: sum(map(flat, L), []) if isinstance(L, list) else [L]
# 拓扑排序
def topo_sort(courses, n):
# 构建图的邻接表
graph_adj = {}
# 元素入度表,默认为0
in_degrees = {x:0 for x in set(flat(courses))}
for x in courses:
if x[1] not in graph_adj:
graph_adj[x[1]] = [x[0]]
else:
graph_adj[x[1]].append(x[0])
in_degrees[x[0]] = in_degrees[x[0]] + 1
queues = []
for x in in_degrees:
if in_degrees[x] == 0:
queues.append(x)
cnt = 0
ret = []
while queues:
top = queues.pop(0)
ret.append(top)
cnt += 1
if top not in graph_adj:
continue
for succ in graph_adj[top]:
in_degrees[succ] -= 1
if in_degrees[succ] == 0:
queues.append(succ)
if cnt == n:
return ret
else:
return []
courses = [
['C3', 'C1'], ['C3', 'C2'],
['C4', 'C3'], ['C4', 'C2'],
['C5', 'C2'],
['C6', 'C5'], ['C6', 'C4'],
['C7', 'C4'], ['C7', 'C9'],
['C8', 'C1'],
['C9', 'C8']
]
# 程序调用
print(topo_sort(courses, 9))