10. 拓扑排序

10.1. AOV 网

AOV 网(Activity On Vertex Network):一个有向图,顶点表示活动,有向边表示活动之间的优先关系。

若顶点 \(u\) 与顶点 \(v\) 之间存在一条弧 \(<u, v>\) ,则表示活动 \(u\) 必须优先于活动 \(v\) 完成,称 \(u\)\(v\) 的直接前驱,\(v\)\(u\) 的直接后继。

10.2. 拓扑序列

\(G=(V, E)\) 是一个 \(n\) 个顶点的有向图,\(V\) 中的顶点序列 \([v_1,v_2,...,v_n]\) 称为一个拓扑序列当且仅当满足下列条件:若从顶点 \(v_i\)\(v_j\) 有路径,则在该顶点 序列中顶点 \(v_i\) 必在 \(v_j\) 之前。

一个 AOV 网的拓扑序列可能不唯一。

10.3. 拓扑排序

算法:

  • Step 1:在 AOV 网中任选一个入度为 0 的顶点(没有直接前驱),输出该顶点;

  • Step 2:在 AOV 网中删除该顶点,并删除这个顶点的所有出边;

  • Step 3:重复前两步,直到 AOV 网中不再有入度为 0 的顶点为止。

这样的操作有两个结果:

  • 网中全部顶点被输出,完成拓扑排序。

  • 网中还有未能输出的顶点,说明网中存在回路,不存在拓扑序列。

图采用邻接表表示法,算法时间复杂度为 \(\mathcal{O}(n + e)\) ;采用邻接矩阵表示法,时间复杂度为 \(\mathcal{O}(n^2)\)

\(\color{darkgreen}{Code}\)

 1// G 用邻接表表示
 2#define MAX_N 50
 3
 4typedef struct ArcNode
 5{
 6  int adjVex;              // 邻接点的下标
 7  WeightType weight;       // 邻边的权重
 8  ArcNode* nextArc;        // 顶点 adjVex 的直接后继
 9}ArcNode;
10
11typedef struct VertexNode
12{
13  VertexType data;           // 顶点的数据
14  ArcNode* firstArc;         // 边链的头指针
15}VertexNode, AdjList[MAX_N];
16
17typedef struct
18{
19  AdjList vertices;         // 顶点数组
20  int vexnum, arcnum;       // 顶点数,边数
21}Graph;
22
23
24// 排序
25void topoSort(Graph G)
26{
27  int cnt = 0; // 已经输出的顶点个数
28  int InDegree[MAX_N] = {0};
29
30  stack<int> S;
31  for(int i = 0; i < G.vexnum; ++i)
32  {
33    for(auto p = G.vertices[i].firstArc; p; p = p->nextArc)
34    {
35      InDegree[p->adjVex] ++; // 统计每个顶点的入度
36    }
37  }
38
39  for(int i = 0; i < G.vexnum; ++i)
40  {
41    if(InDegree[i] == 0) S.push(i);
42  }
43
44  while(! S.empty())
45  {
46    int v = S.top();
47    S.pop();
48    cout << v;
49    cnt ++;
50    for(auto p = G.vertices[v].firstArc; p; p = p->nextArc)
51    {
52      int u = p->adjVex;
53      InDegree[u] --; // v 的所有出边入度减 1
54      if(InDegree[u] == 0) S.push(u);
55    }
56  }
57
58  if(cnt < G.vexnum) cout << "存在环";
59}