拓扑排序(TopoSort)有相当广泛的应用,比如依赖关系分析、图是否有环的检测、编译优化中的数据流分析等。
拓扑排序,就是任意一条边 u->v,节点 u 都先于节点 v 。
显然,有向图中有环的话,就不存在拓扑排序,所以我们讲的拓扑排序是针对有向无环图(DAG)的。拓扑排序可能不是唯一的,如下图,ABDCEF 、ABDECF 和 ABCDEF 都是这个 DAG 的拓扑排序。
上文 图的邻接矩阵、邻接表存储和图的广度优先搜索(BFS)、深度优先搜索(DFS)介绍 BFS 和 DFS 算法时,使用的是图的邻接表存储方式,这次我使用邻接矩阵存储方式来介绍拓扑排序。完整的代码放到文章最后,方便大家验证。
#define MaxVertexNum 10
typedef char VertexType;
typedef int EdgeType;
typedef struct
{
VertexType vertex[MaxVertexNum];
EdgeType edge[MaxVertexNum][MaxVertexNum];
int vexnum, edgenum;
} AdjMatGraph;
Kahn 算法
首先介绍的是 Kahn 算法,Kahn 算法非常简单,根据拓扑序很容易理解。DAG 中有一条边 u->v,则 u 要排在 v 前面,那么 ADG 中入度为 0 的顶点,则不会依赖任何其他顶点,可以排在前面。算法基本思想:首先找到入度为 0 的顶点,将其输出到序列中,并将该顶点删除,则该顶点指向的顶点入度相应减 1。重复上述过程,直到完成所有顶点的排序,最后得到的序列就是拓扑排序。算法实现如下:
void kahnTopoSort(const AdjMatGraph& g) {
int* indegree = new int[g.vexnum];
memset(indegree, 0, g.vexnum * sizeof(int));
for (int i = 0; i < g.vexnum; i++) {
for (int j = 0; j < g.vexnum; j++) {
if (g.edge[i][j] == 1) {
indegree[j] += 1;
}
}
}
std::queue<int> q;
for (int i = 0; i < g.vexnum; i++) {
if (indegree[i] == 0) {
q.push(i);
}
}
while (!q.empty()) {
int u = q.front();
q.pop();
// visit
std::cout << g.vertex[u];
for (int j = 0; j < g.vexnum; j++) {
if (g.edge[u][j] == 1) {
indegree[j] -= 1;
if (indegree[j] == 0) {
q.push(j);
}
}
}
}
delete [] indegree;
}
DFS 算法
第二种算法是使用 DFS 算法遍历图,并且在回溯的时候将遍历的顶点入栈,那么先入栈的顶点必定入度不为 0,而入度为 0 的顶点必定是最后入栈。算法实现如下:
void dfs(const AdjMatGraph& g, const int u, bool visited[], std::stack<int>& s) {
visited[u] = true;
for (int j = 0; j < g.vexnum; j++) {
if (g.edge[u][j] == 1 && !visited[j]) {
dfs(g, j, visited, s);
}
}
s.push(u);
}
void dfsTopoSort(const AdjMatGraph& g) {
std::stack<int> s;
bool *visited = new bool[g.vexnum];
for (int i = 0; i < g.vexnum; i++) {
if (!visited[i]) {
dfs(g, i, visited, s);
}
}
// visit
while (!s.empty()) {
int v = s.top();
std::cout << g.vertex[v];
s.pop();
}
std::cout << std::endl;
delete [] visited;
}
最后附上完整代码方便测试:
#include<iostream>
#include<cstring>
#include<queue>
#include<stack>
#define MaxVertexNum 50
typedef char VertexType;
typedef int EdgeType;
typedef struct
{
VertexType vertex[MaxVertexNum];
EdgeType edge[MaxVertexNum][MaxVertexNum];
int vexnum, edgenum;
} AdjMatGraph;
// lookup index of vertex u in the graph
int LocateVex(const AdjMatGraph& g, VertexType u)
{
int i;
for(i = 0; i < g.vexnum; ++i) {
if(u == g.vertex[i])
{
return i;
}
}
return -1;
}
void printAdjMatGraph(const AdjMatGraph& g) {
for (int i = 0; i < g.edgenum; i++) {
for (int j = 0; j < g.edgenum; j++) {
if (g.edge[i][j] == 1) {
std::cout << g.vertex[i] << " -> " << g.vertex[j] << std::endl;
}
}
}
}
void createAdjMatGraph(AdjMatGraph& g) {
std::cout << "Enter vertex num: ";
std::cin >> g.vexnum;
std::cout << "Enter edge num: ";
std::cin >> g.edgenum;
std::cout << "Enter " << g.vexnum << " vertex value: ";
for (int i = 0; i < g.vexnum; i++) {
std::cin >> g.vertex[i];
}
memset(g.edge, 0, g.vexnum * g.vexnum * sizeof(EdgeType));
std::cout << "Enter " << g.edgenum << " edge info:" << std::endl;
for (int e = 0; e < g.edgenum; e++) {
VertexType v1, v2;
std::cin >> v1 >> v2;
int i = LocateVex(g, v1);
int j = LocateVex(g, v2);
g.edge[i][j] = 1;
}
}
void kahnTopoSort(const AdjMatGraph& g) {
int* indegree = new int[g.vexnum];
memset(indegree, 0, g.vexnum * sizeof(int));
for (int i = 0; i < g.vexnum; i++) {
for (int j = 0; j < g.vexnum; j++) {
if (g.edge[i][j] == 1) {
indegree[j] += 1;
}
}
}
std::queue<int> q;
for (int i = 0; i < g.vexnum; i++) {
if (indegree[i] == 0) {
q.push(i);
}
}
while (!q.empty()) {
int u = q.front();
q.pop();
// visit
std::cout << g.vertex[u];
for (int j = 0; j < g.vexnum; j++) {
if (g.edge[u][j] == 1) {
indegree[j] -= 1;
if (indegree[j] == 0) {
q.push(j);
}
}
}
}
std::cout << std::endl;
delete [] indegree;
}
void dfs(const AdjMatGraph& g, const int u, bool visited[], std::stack<int>& s) {
visited[u] = true;
for (int j = 0; j < g.vexnum; j++) {
if (g.edge[u][j] == 1 && !visited[j]) {
dfs(g, j, visited, s);
}
}
s.push(u);
}
void dfsTopoSort(const AdjMatGraph& g) {
std::stack<int> s;
bool *visited = new bool[g.vexnum];
for (int i = 0; i < g.vexnum; i++) {
if (!visited[i]) {
dfs(g, i, visited, s);
}
}
// visit
while (!s.empty()) {
int v = s.top();
std::cout << g.vertex[v];
s.pop();
}
std::cout << std::endl;
delete [] visited;
}
int main() {
AdjMatGraph g;
createAdjMatGraph(g);
printAdjMatGraph(g);
kahnTopoSort(g);
dfsTopoSort(g);
return 0;
}