一、AOV网的定义
(activity on vertex network)
1.在一个表示表示工程的有向图中,顶点表示活动,用弧来表示优先关系。
2.AOV网中出现回路——意味着活动之间的优先关系时矛盾的。
3.有向无环图(DAG图):指不存在回路的有向图,显然,AOV图一定是有向图。
二、拓扑排序的定义
1.设有向图G=(v,e)有n个顶点
则拓扑序列是满足以下条件的顶点序列v0,v1,v2...v(n-1)【注意:拓扑序列要包含有向图的所有顶点】:
若从两个顶点v(i)和v(j)之间有一条路径,则顶点序列中v(i)必须在v(j)之前。
2.拓扑排序的作用:使得AOV网中的前去和后继关系都能得到满足。
例:判断下面那个不满足拓扑排序
三、拓扑排序算法——基本思想
算法:深度优先拓扑排序TopSort
输入:AOV网G=(V,E)
输出:拓扑序列
1.重复一下操作,直到输出全部顶点,或AOV网中不存在没有前驱的顶点
1.1.从AOV网中选择一个没有前驱的顶点并且输出;
1.2.将此顶点从AOV网中删除,并且删除所有以该顶点为尾的弧;
提示:不管是广度优先拓扑排序还是深度优先拓扑排序,基本思想都是这个。
四、逆拓扑排序基本思想
重复一下操作,直到输出全部顶点,或AOV网中不存在没有出度的顶点
1.选择一个无出度的顶点并输出
2.将此顶点从AOV网中删除,并且删除所有以该顶点为头的弧。
五、拓扑排序算法——储存结构
储存结构——邻接表
如何求顶点的入度——顶点表中增加度域。
如图
六、拓扑排序——算法
深度优先拓扑排序
深度优先主要是递归思想,也就是“后被访问的顶点的邻接点”优先于“先被访问的顶点的邻接点”。
用到以下两个算法:
1.算法:dfstoplogocalSort
输入:顶点数n
输出:topo序列
1.对每一个顶点k
1.1.if(k的入度为零且未被访问)对k进行深度优先遍历;
2.算法:DFS
输入:开始结点u
输出:深度优先遍历序列
1.将顶点u设为已访问,输出顶点u;
2.对u的每个邻接点i执行下述操作:
2.1.将i的入度减一;
2.2.if(i的入度为0并且未被访问)
对顶点i递归进行深度优先遍历
广度优先拓扑排序
广度优先拓扑排序的主要思想是利用栈或队列辅助,“先被访问的顶点的邻接点”先于“后被访问的顶点的邻接点”。
算法:bfstopologicalSort
输入:顶点数n
输出:topo序列
1.初始化队列q;
2.对每一个顶点k
2.1.if(k的入度为0)
将k压入队列q中;
3.队列不为空时(队列中还有入度为0的顶点时)
3.1.u=队头元素出队;输出顶点u;
3.2.对u的每一个邻接点v执行下述操作:
3.2.1.将v的入度减一;
3.2.2.如果入度为0,压入队列中;
七、C++代码实现
1.深度优先
#include <iostream>
#include <vector>
using namespace std;
const int MAXN = 10010;
vector<int> G[MAXN];
int inDegree[MAXN];
bool vis[MAXN];
void dfs(int u) {
vis[u] = true;
cout << u << " ";
for (int i = 0; i < G[u].size(); i++) {
int v = G[u][i];
inDegree[v]--;
if (inDegree[v] == 0 && !vis[v]) {
dfs(v);
}
}
}
bool topologicalSort(int n) {
for (int i = 1; i <= n; i++) {
if (inDegree[i] == 0 && !vis[i]) {
dfs(i);
}
}
return true;
}
int main() {
int n, m;
cin >> n >> m;
for (int i = 1; i <= m; i++) {
int u, v;
cin >> u >> v;
G[u].push_back(v);
inDegree[v]++;
}
topologicalSort(n);
return 0;
}
2.广度优先
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
const int MAXN = 10010;
vector<int> G[MAXN];
int inDegree[MAXN];
bool topologicalSort(int n) {
queue<int> q;
for (int i = 1; i <= n; i++) {
if (inDegree[i] == 0) {
q.push(i);
}
}
while (!q.empty()) {
int u = q.front();
q.pop();
cout << u << " ";
for (int i = 0; i < G[u].size(); i++) {
int v = G[u][i];
inDegree[v]--;
if (inDegree[v] == 0) {
q.push(v);
}
}
}
return true;
}
int main() {
int n, m;
cin >> n >> m;
for (int i = 1; i <= m; i++) {
int u, v;
cin >> u >> v;
G[u].push_back(v);
inDegree[v]++;
}
topologicalSort(n);
return 0;
}
实例:
1.
2.
【注】:这里我利用向量来储存顶点以及邻接点,但是比较在数据结构中比较建议实用邻接表来实现。前面说到广度优先拓扑排序是可以用栈和队列辅助实现的,虽然两种方法作用大致相同,但是由于栈是先进后出、队列是先进先出,所以最后得到的拓扑序列肯定会有所差异,具体要看题目的要求。
3.下面给出使用邻接表和栈的广度优先拓扑序列C++实现
void TopSort()
{
int i,j.k.count=0;S[MaxSize],top=-1;
EdgeNode*p=nullptr;
for(int i=0;i<vertexNum;i++)
if(adjist[i].in==0)s[top++]=i;
while(top!=-1)
{
j=S[top--];cout<<adjist[j].vertex;count++;
p=adjlist[j].first;
while(p!=nullptr)
{
k=p->adjvex;S[top++]=k;
p=p->next;
}
}
}
时间复杂度为O(n+e);
八、应用
可以用来判断图中是否存在回路,如果存在回路,则应用拓扑排序后必然会有结点未被处理,即count<vertexNum