文章目录
拓扑排序主要可以判断有向无环图(DAG)中是否存在环路,其可以用来判断一个有着先后关系的工程能否顺利进行。 |
1. 有向无环图(DAG)
一个不存在环路的有向图称作有向无环图(Directed Acycline Graph),简称DAG图。
判断一个图是否存在环路可以有以下两种思路:
- 对于无向图,若深度优先遍历过程中遇到回边(即指向已经访问过的顶点的边),则必存在环路;
- 对于有向图,可以构造其顶点的拓扑排序序列,若图中所有顶点都在它的拓扑排序序列中,则必不存在环路;
2. 拓扑排序
2.1 拓扑排序的概念
**拓扑排序(Topological Sort)**即由一个集合上的一个偏序得到该集合上的一个全序。
(偏序是值集合中仅有部分成员之间可比较,而全序指集合中全体成员之间均可比较)
2.2.拓扑排序的基本思想
拓扑排序的算法如下:
- 1)在有向图中选择一个没有前驱(入度为0)的顶点输出;
- 2)从图中删除该顶点和所有以它为尾的弧;
- 3)重复(1)(2),直至全部顶点均已输出,或当前图不存在无前驱的顶点为止。
后一种情况表示有向图中存在环。
3.拓扑排序的实现
3.1 有向图的邻接表实现
若采用邻接表作有向图的存储结构,则可以在头结点数组中增加一个存放顶点入度的数组,入度为0的顶点即没有前驱的顶点,删除顶点以以它为尾的操作可以等价于以弧头当点的入度减1来实现。
同时,为了避免重复检测入度为0的顶点,可以另设一个栈来存储所有入度为0的顶点.
3.1.1 图的邻接表存储
邻接表定义
typedef struct node {
//边节点
int adjvex; // 顶点下标
struct node* nextarc; // 指向下一顶点
}node;
typedef struct headNode{
// 表头节点
int indegree; // 入度
string info; // 顶点的信息,如名字
node* firstarc; // 第一邻接点
}headNode,Adjlist[MAX_VERTEX_NUM];
// 邻接表
typedef struct {
Adjlist vertex; // 头节点表
int vexnum, arcnum; // 点的数目和边的数目
}ALGraph;
创建邻接表
void createALGraph(ALGraph &G) {// 创建邻接表
printf("输入顶点数目和边的数目:\n");
cin >> G.vexnum >> G.arcnum;
printf("输入顶点信息:\n");
for (int i = 0; i < G.vexnum; i++) {
cin >> G.vertex[i].info;
G.vertex[i].indegree = 0;
G.vertex[i].firstarc = NULL;
}
printf("输入边的信息,以V1->V2的形式:\n");
for (int i = 0; i < G.arcnum; i++) {
string v1, v2;
cin >> v1 >> v2;
int index1 = Locate(G, v1);
int index2 = Locate(G, v2);
G.vertex[index2].indegree += 1;
// 使用头插法插入新节点
node* temp = new node;
temp->adjvex = index2;
temp->nextarc = G.vertex[index1].firstarc;
G.vertex[index1].firstarc = temp;
}
}
找邻接表中某节点的下标
int Locate(ALGraph G, string v) {
int i = 0;
for (; i < G.vexnum && G.vertex[i].info != v; i++);
if (i == G.vexnum) return -1;
else return i;
}
3.1.2 邻接表的拓扑排序
int TopoSort(ALGraph G) {
// 对各个顶点求入度 indegree[0..vexnum-1]
// 若初始化图的时候完成,则可省略
//FindInDegree(G, indegree);
stack<int> s; // 入度为0的节点的下标
for (int i = 0; i < G.vexnum; i++) {
if (G.vertex[i].indegree == 0) s.push(i);
}
// 打印不为0的节点
int count = 0;// 当前已经打印的节点
while (!s.empty()) {
int j = s.top(); s.pop();
//printf("当前找到的顶点%d ", j);
count++;
//cout << G.vertex[j].info << "-->" << endl;
printf("%s-->",G.vertex[j].info.c_str());
// 删除以该节点为尾的弧
node* temp = G.vertex[j].firstarc;
while (temp) {
int index = temp->adjvex;
G.vertex[index].indegree--;
if (G.vertex[index].indegree == 0) s.push(index);
temp = temp->nextarc;
}
}
if (count < G.vexnum) return 0; // 有环路
else return 1; // 无环路
}
3.2 有向图的邻接矩阵实现
3.2.1 图的邻接矩阵存储
邻接矩阵的定义
#define MAX_VERTEX_NUM 20 // 最大的顶点数目
// 定义邻接矩阵
typedef int AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; //0 or 1表示是否邻接
typedef struct {
AdjMatrix matrix;
string vexs[MAX_VERTEX_NUM]; // 顶点向量
int indegree[MAX_VERTEX_NUM]; // 顶点的入度
int vexnum, arcnum; // 顶点数和边的数目
}MGraph;
邻接矩阵的创建
void createMGraph(MGraph &G) {
int i, j;
printf("输入顶点数和边的数目:\n");
cin >> G.vexnum >> G.arcnum;
// 初始化邻接矩阵
for (i = 0; i < G.vexnum; i++) {
G.indegree[i] = 0;
for (j = 0; j < G.vexnum; j++) G.matrix[i][j] = 0;
}
printf("输入顶点信息\n");
for (i = 0; i < G.vexnum; i++) cin >> G.vexs[i];
printf("输入边的信息Vi-->Vj\n");
for (i = 0; i < G.arcnum; i++) {
string v1, v2;
cin >> v1 >> v2;
int l1 = LocateVex(G, v1);
int l2 = LocateVex(G, v2);
G.matrix[l1][l2] = 1;
G.indegree[l2]++;
}
}
顶点的定位
int LocateVex(MGraph G, string u) {
int i;
for (i = 0; i < G.vexnum && G.vexs[i] != u; i++);
if (i == G.vexnum) return -1;
else return i;
}
3.2.2 邻接矩阵的拓扑排序
int TopoSort(MGraph G) {
int i, j;
stack<int> s;
for (i = 0; i < G.vexnum; i++) {
if (G.indegree[i] == 0) s.push(i);
}
int count = 0; // 已排序的顶点
while (!s.empty()) {
j = s.top(); s.pop();
count++;
cout << G.vexs[j] << "-->";
for (int k = 0; k < G.vexnum; k++) {
if (G.matrix[j][k] == 1) {
if (!(--G.indegree[k])) s.push(k); // 删除弧
}
}
}
if (count == G.vexnum) return 0;
else return 1;
}
3.3 测试用例
6 8
V1 V2 V3 V4 V5 V6
V1 V2
V1 V4
V1 V3
V3 V2
V3 V5
V4 V5
V6 V4
V6 V5
3.4 基于深度优先的拓扑排序
当有向图中无环的时,可以利用深度优先遍历进行拓扑排序,即从某点出发进行深度优先遍历的时候,
最先退出深度优先遍历的顶点即出度为0的顶点,是拓扑排序序列中最后一个顶点。
因此,按照退出DFS的先后顺序记录下顶点序列,再逆序输出即可。
#include <iostream>
#include <string>
#include<stack>
using namespace std;
#define MAX_VERTEX_NUM 20 // 最大的顶点数目
// 定义邻接矩阵
typedef int AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; //0 or 1表示是否邻接
typedef struct {
AdjMatrix matrix;
string vexs[MAX_VERTEX_NUM]; // 顶点向量
int indegree[MAX_VERTEX_NUM]; // 顶点的入度
int vexnum, arcnum; // 顶点数和边的数目
}MGraph;
//图的创建
void createMGraph(MGraph &G);
// 顶点的定位
int LocateVex(MGraph G, string v);
// 返回图G中u的第一个邻接点
int First(MGraph g, int u);
// 返回图G中V相对于u的下一邻接点
int Next(MGraph G, int v, int u);
// 拓扑排序
void DFS_Master(MGraph G);
void DFS(MGraph G, int v, int* visited, stack<int> &s);
int main() {
MGraph G;
createMGraph(G);
DFS_Master(G);
system("pause");
return 0;
}
void DFS_Master(MGraph G) {
// 初始化访问节点
int i, j;
int* visited = new int[G.vexnum];
// 逆序节点
stack<int> s;
for (i = 0; i < G.vexnum; i++) visited[i] = 0;
for (i = 0; i < G.vexnum; i++) {
if (!visited[i]) DFS(G, i, visited,s);
}
while (!s.empty()) {
j = s.top(); s.pop();
cout << G.vexs[j] << "-->";
}
delete[] visited;
}
void DFS(MGraph G, int v, int* visited,stack<int> &s) {
visited[v] = 1;
printf("访问%d节点", v);
int i;
for (i = First(G, v); i != -1; i = Next(G, v, i)) {
if (!visited[i]) DFS(G, i, visited, s);
}
// 如果v没有邻接点i,那么它就会退出
s.push(v);
}
void createMGraph(MGraph &G) {
int i, j;
printf("输入顶点数和边的数目:\n");
cin >> G.vexnum >> G.arcnum;
// 初始化邻接矩阵
for (i = 0; i < G.vexnum; i++) {
G.indegree[i] = 0;
for (j = 0; j < G.vexnum; j++) G.matrix[i][j] = 0;
}
printf("输入顶点信息\n");
for (i = 0; i < G.vexnum; i++) cin >> G.vexs[i];
printf("输入边的信息Vi-->Vj\n");
for (i = 0; i < G.arcnum; i++) {
string v1, v2;
cin >> v1 >> v2;
int l1 = LocateVex(G, v1);
int l2 = LocateVex(G, v2);
G.matrix[l1][l2] = 1;
G.indegree[l2]++;
}
}
int LocateVex(MGraph G, string u) {
int i;
for (i = 0; i < G.vexnum && G.vexs[i] != u; i++);
if (i == G.vexnum) return -1;
else return i;
}
int First(MGraph G, int u) {
int i;
for (i = 0; i < G.vexnum; i++) {
if (G.matrix[u][i] != 0) break;
}
if (i == G.vexnum) return -1; // 没有临界点
else return i;
}
int Next(MGraph G, int v, int u) {
int index;
for (index = u + 1; index < G.vexnum && G.matrix[v][index] == 0; index++);
if (index == G.vexnum) return -1;
else return index;
}
参考资料
《数据结构 C语言版》 严蔚敏著
来源:https://blog.csdn.net/Africa_South/article/details/90232885