1. 注:本代码中的思想来源于s1mba。膜拜大神!
2. 本代码在邻接矩阵的基础上创建邻接表。这样做的方便性就是,在邻接矩阵中我们可以很直观的表示出两个顶点是否相连,而且通过是不是对称矩阵的方式区分是不是有向图。这一点在创建邻接表时很重要,用 if (G.arcs[ i ][ j ] == 1)判断是不是要创建新的表结点。
3. 另外,在邻接表的顶点表中外加一个入度域in也是点睛之笔,这是为了方便判断结点入度是否为0,从而决定是否入栈。
4. By the way,本代码中用的是STL库中的stack容器,更加方便。用数组还要用下标index移来移去,边界条件什么的感觉很麻烦。虽然数组可能速度更快
5. TopologicalSort的思想映射在邻接表数据结构上的表现要好好思考。先遍历一遍邻接表,把入度已经为0的结点内容先入栈,然后从栈中开始一个一个的选,用while (!ZeroDegreeNodeStack.empty())来做循环条件,从栈中的结点那一行链表开始遍历,后面链表中每个结点的入度都 -- ,然后判断 -- 后是否为0,如果为0就入栈。
6. 在上面遍历的时候要注意一点就是,输出了栈顶的结点内容之后,确实要出栈,然后记录出栈结点数cnt++,但是要用一个辅助变量cur来保存栈顶元素的值用于for循环中的初始条件。
7. 千万不要把stack.pop()操作放在for循环的后面,因为这样就会导致在for循环中入栈的元素直接就出栈了,还没输出呢就出栈了,从逻辑上就是错误的!
上代码:
#include <iostream>
#include <stack>
using namespace std;
/**
* 1.用邻接矩阵创建图 但是由于在拓扑排序中需要进行删除顶点操作, 所以用邻接表会更加方便
* 因此我们创建好图的邻接矩阵后,再用邻接矩阵创建邻接表
* 2.由于需要经常判断结点的入度是否为0,所以我们在邻接表的顶点表中加入一个存放入度的数据域in
*/
#define MaxVertexNum 100
#define Infinity 10000
//邻接矩阵结构定义
typedef struct {
int vers[MaxVertexNum]; //顶点数组
int arcs[MaxVertexNum][MaxVertexNum]; //边数组
int vernum, arcnum; //点和边的数目
}AMGraph;
//邻接表的表结点结构定义
typedef struct EdgeNode {
int adjver; //存储该邻接点在顶点表中的下标
struct EdgeNode* next; //指向下一个邻接点的指针域
}EdgeNode;
//邻接表的顶点表结构定义
typedef struct {
int in; //入度域
int data; //存放结点的内容,在本实验AOV网中存放的是int值
EdgeNode* firstEdge; //指向第一个邻接结点的指针域
}VertexNode, AdjVerList[MaxVertexNum];
//邻接表的结构体定义
typedef struct {
AdjVerList adjverList;
int vernum, arcnum; //顶点数和边数
}adjlist, *AdjList; //前面一个不带指针的是为了分配空间
void CreatAM(AMGraph &G) { //初始化邻接矩阵
cout << "请输入该图的顶点数和边数:" << endl;
cin >> G.vernum >> G.arcnum;
for (int i = 1; i <= G.vernum; i++) {
G.vers[i] = i;
}
for (int i = 1; i <= G.vernum; i++) {
for (int j = 1; j <= G.vernum; j++) {
G.arcs[i][j] = 0;
}
}
G.arcs[1][2] = 1;
G.arcs[1][3] = 1;
G.arcs[2][4] = 1;
G.arcs[3][4] = 1;
G.arcs[4][5] = 1;
}
void CreatAdjList(AMGraph G, AdjList &AL) { //创建邻接表
EdgeNode* p;
AL = new adjlist;
AL->vernum = G.vernum;
AL->arcnum = G.arcnum;
for (int i = 1; i <= G.vernum; i++) { //初始化
AL->adjverList[i].data = G.vers[i]; //结点信息复制
AL->adjverList[i].in = 0;
AL->adjverList[i].firstEdge = NULL;
}
for (int i = 1; i <= G.vernum; i++) {
for (int j = 1; j <= G.vernum; j++) {
if (G.arcs[i][j] == 1){ //如果有边
p = new EdgeNode;
p->adjver = j; //该邻接点在顶点表中的下标
p->next = AL->adjverList[i].firstEdge; //头插法
AL->adjverList[i].firstEdge = p;
AL->adjverList[j].in++; //注意这里是j!
}
}
}
}
void TopologicalSort(AdjList AL) {
EdgeNode* p;
int cnt = 0; //统计已经删除过的结点个数 用于判断是不是个有向无环图
stack<int> ZeroDegreeNodeStack; //存放入度为0的栈
for (int i = 1; i <= AL->vernum; i++) {
if (AL->adjverList[i].in == 0) {
ZeroDegreeNodeStack.push(i); //把结点的内容入栈
}
}
while (!ZeroDegreeNodeStack.empty()) {
cout << ZeroDegreeNodeStack.top() << "->"; //输出结点内容
int cur = ZeroDegreeNodeStack.top(); //辅助变量
ZeroDegreeNodeStack.pop();
cnt++;
for (p = AL->adjverList[cur].firstEdge; p != NULL; p = p->next) {
AL->adjverList[p->adjver].in--;
if (AL->adjverList[p->adjver].in == 0) { //如果删除掉这个节点,入度减1后变为0,那就入栈
ZeroDegreeNodeStack.push(p->adjver);
}
}//for
}//while
cout << endl;
if (cnt == AL->vernum)
cout << "该AOV网是一个有向无环图!" << endl;
else
cout << "该AOV网不是一个有向无环图" << endl;
}
int main() {
AMGraph G; //邻接矩阵
AdjList AL; //邻接表
CreatAM(G); //创建邻接矩阵
CreatAdjList(G, AL); //在邻接矩阵的基础上创建邻接表
cout << "拓扑排序的结果为:" << endl;
TopologicalSort(AL);
}