图——基本的图算法(三)拓扑排序
1. 基本概念
对于一个有向无环图G = (V, E)来说,其拓扑排序就是G中所有顶点的一种线性排序,这种排序满足如下条件:如果图G中包含边(a, b),即由顶点a指向顶点b的有向边,那么在G的拓扑排序中,顶点a一定处于顶点b前面(因此如果有向图G中包含回路,则不可能排出这样一个线性次序)。
2. 算法实现
2.1 总体思想
对一个有向无环图进行拓扑排序的基本思路是:从图中选出一个入度为0的顶点输出,然后删掉这个顶点,并且删去以这个顶点为尾的弧,然后重复上述步骤,直至全部顶点输出。
2.2 数据结构
对于拓扑排序,由于要删掉以某个入度为0的顶点为尾的弧,也就是要删掉相应的边,因此一般采用邻接表来对图进行表示,但要稍作修改:顶点表中的每个结点增加一项用来表示该顶点此时的入度值。具体如下:
(1)顶点表结点
typedef struct VertexListNode{
int in; //入度
int data; //该顶点的值
EdgeListNode* firstadj; //指向该顶点的边表里的第一个结点
};
(2)边表结点
typedef struct EdgeListNode{
int adjId; //弧头在顶点表中的下标
int weight; //弧的权值
EdgeListNode* next; //指向边表中的下一个结点
};
(3)邻接链表表示的图结构
typedef struct GraphAdjList{
int vertexnumber; //顶点个数
int edgenumber; //弧的条数
VertexListNode vertextlist[Maximum]; //顶点表
};
(4)综上,数据结构为:
#define Maximum 1000
typedef struct EdgeListNode{
int adjId;
int weight;
EdgeListNode* next;
};
typedef struct VertexListNode{
int in;
int data;
EdgeListNode* firstadj;
};
typedef struct GraphAdjList{
int vertexnumber;
int edgenumber;
VertexListNode vertextlist[Maximum];
};
2.3 具体实现
// 返回存储了拓扑排序的vector
vector<int> ToplogicalSort(GraphAdjList g) {
int ans = 0;
queue<int>q; //用来存储入度为0的待处理顶点
vector<int> sort_queue; //用来存储拓扑排序结果
// 清空队列
while(!q.empty()) {
q.pop();
}
int i, j, k;
//找出所有入度为0的顶点,入队列
for(i=1; i<=g.vertexnumber; i++) {
if(g.vertextlist[i].in == 0) {
q.push(i);
}
}
EdgeListNode *temp;
while(!q.empty()) {
j = q.front();
q.pop();
ans++;
sort_queue.push_back(j);
temp = g.vertextlist[j].firstadj;
//遍历其边表,找出于其有边相连的顶点
while(temp != NULL) {
g.vertextlist[temp->adjId].in--; //该顶点入度减一
if(g.vertextlist[temp->adjId].in == 0) {
q.push(temp->adjId);
}
temp = temp->next;
}
}
if(ans < g.vertexnumber) { //这是一个有环有向图
sort_queue.clear(); //清空vector
}
return sort_queue;
}
2.4 测试
完整代码:
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<map>
#include<vector>
#include<sstream>
#include<list>
#include<stdlib.h>
#include<queue>
using namespace std;
#define Maximum 1000
typedef struct EdgeListNode{
int adjId;
int weight;
EdgeListNode* next;
};
typedef struct VertexListNode{
int in;
int data;
EdgeListNode* firstadj;
};
typedef struct GraphAdjList{
int vertexnumber;
int edgenumber;
VertexListNode vertextlist[Maximum];
};
vector<int> ToplogicalSort(GraphAdjList g) {
int ans = 0;
queue<int>q;
vector<int> sort_queue;
while(!q.empty()) {
q.pop();
}
int i, j, k;
for(i=1; i<=g.vertexnumber; i++) {
if(g.vertextlist[i].in == 0) {
q.push(i);
}
}
EdgeListNode *temp;
while(!q.empty()) {
j = q.front();
q.pop();
ans++;
sort_queue.push_back(j);
temp = g.vertextlist[j].firstadj;
while(temp != NULL) {
if(--g.vertextlist[temp->adjId].in == 0) {
q.push(temp->adjId);
}
temp = temp->next;
}
}
if(ans < g.vertexnumber) {
sort_queue.clear();
}
return sort_queue;
}
int main() {
GraphAdjList g;
g.vertexnumber = 5;
g.edgenumber = 5;
int i;
for(i=1; i<6; i++) {
g.vertextlist[i].data = i;
g.vertextlist[i].firstadj = NULL;
}
g.vertextlist[1].in = g.vertextlist[4].in = 0;
g.vertextlist[5].in = g.vertextlist[3].in = 2;
g.vertextlist[2].in = 1;
EdgeListNode* t;
t = (EdgeListNode*)malloc(sizeof(EdgeListNode));
t->adjId = 3; t->next = NULL; g.vertextlist[1].firstadj = t;
t = (EdgeListNode*)malloc(sizeof(EdgeListNode));
t->adjId = 2; t->next = g.vertextlist[1].firstadj; g.vertextlist[1].firstadj = t;
t = (EdgeListNode*)malloc(sizeof(EdgeListNode));
t->adjId = 5; t->next = NULL; g.vertextlist[2].firstadj = t;
t = (EdgeListNode*)malloc(sizeof(EdgeListNode));
t->adjId = 5; t->next = NULL; g.vertextlist[3].firstadj = t;
t = (EdgeListNode*)malloc(sizeof(EdgeListNode));
t->adjId = 3; t->next = NULL; g.vertextlist[4].firstadj = t;
vector<int>v;
v = ToplogicalSort(g);
if(v.size() == 0) {
cout<<"This graph has ring"<<endl;
}
else {
for(i=0; i<v.size(); i++) {
cout<<v[i]<<" ";
}
cout<<endl;
}
return 0;
}