拓扑排序:即对有向无环图进行排序,使得排序后所得系列满足如下条件,凡是是<s,t>属于DAG图则有s在序列中位于t的前方,这样得到的序列称为拓扑排序序列,那么有两种方法构建拓扑排序。条件是:该有向图必须是无环图即DAG图。
一种是Kahn算法:(伪代码)
L← Empty list that will contain the sorted elements
S ← Set of all nodes with no incoming edges
while S is non-empty do
remove a node n from S
insert n into L
foreach node m with an edge e from nto m do
remove edge e from thegraph
ifm has no other incoming edges then
insert m into S
if graph has edges then
return error (graph has at least onecycle)
else
return L (a topologically sortedorder)
该算法的核心是将原图中入度为0的顶点存入栈或队列,然后任意取出一个顶点,去点这个顶点以及与其相连的所有边,如果与其相连的顶点的入度在去掉这条边后入度为0则入栈或队列,不断循环执行,最终判断图中是否还有没有去掉的边,如有则至少存在一个环,否则排序成功。
下面是代码:
#include <queue>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string.h>
using namespace std;
#define Max 101
typedef int type_data;
int coun[Max]; //入度记录数组
int record[Max-1];//拓扑排序系列
//bool visit[Max];
typedef struct Arc{ //边结点
int number;
int value;
struct Arc *next;
}ArcNode;
typedef struct{ //表头结点
type_data data;
ArcNode *first;
}VertexNode;
typedef struct{ //图
int n;//结点数
int m;//边数
VertexNode tex[Max];
}AdjList;
void Creat_grap(AdjList *g){
scanf("%d%d",&g->n,&g->m);
memset(coun,0,sizeof(coun));
int i,s,t,val;
for(i=1;i<=g->n;i++){
scanf("%d",&g->tex[i].data);
g->tex[i].first=NULL;
}
for(i=0;i<g->m;i++){
scanf("%d%d%d",&s,&t,&val);
coun[t]++;
ArcNode *p=(ArcNode*)malloc(sizeof(ArcNode));
p->number=t;
p->value=val;
p->next=g->tex[s].first;
g->tex[s].first=p;
/* ArcNode *q=(ArcNode*)malloc(sizeof(ArcNode)); //当为有向图时不能写
q->number=s;
q->value=val;
q->next=g->tex[t].first;
g->tex[t].first=q;*/
}
}
void Clear(AdjList *g){
for(int i=1;i<=g->n;i++){
ArcNode *p=g->tex[i].first;
while(p){
ArcNode *q=p->next;
free(p);
p=q;
}
}
}
bool Insert_kahn(AdjList *g){
queue<int> que;
for(int i=1;i<=g->n;i++)
if(coun[i]==0)
que.push(i);
int num=0,index=0;
while(!que.empty()){
int temp=que.front();
que.pop();
record[index++]=temp;
ArcNode *q=g->tex[temp].first;
while(q){
if(--coun[q->number]==0)
que.push(q->number);
q=q->next;
num++;
}
}
if(num<g->m){
printf("having a circle\n");
return false;
}
return true;
}
int main(){
AdjList g;
Creat_grap(&g);
/*for(int i=1;i<=g.n;i++){
//printf("skjdfhsdkh\n");
ArcNode *p=g.tex[i].first;
while(p){
printf("%d ",p->number);
p=p->next;
}
printf("\n");
}
Ebfs(&g);
Clear(&g);*/
if(Insert_kahn(&g))
for(int i=0;i<g.n;i++)
printf("%d ",record[i]);
Clear(&g);
return 0;
}
另一种方法为dfs的方法,那么这种方法的前提是已知图为DAG才可排序,否则会出错,基于这种算法的图的存储结构也由原来的临界表转换为邻接矩阵,给算法的核心是:
用栈或者队列存储出度为0的顶点,设置标记数组,如没有访问则访问,而访问则用dfs函数实现,对于该函数提供的每一个顶点,遍历所有顶点,若遍历的顶点没有指向该顶点
并且没有访问,则递归访问,在遍历完后还要讲提供顶点如栈或队列。
下面是伪代码:
L ← Empty list that will contain the sorted nodes
S ← Set of all nodes with no outgoing edges
for each node n in S do
visit(n)
function visit(node n)
if n has not been visited yet then
mark n as visited
for each node m with an edgefrom m to ndo
visit(m)
add n to L
下面是不完整代码(供参考):
#include <queue>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string.h>
using namespace std;
#define Max 101
typedef int type_data;
bool coun[Max]; //出度记录数组
//int record[Max-1];//拓扑排序系列
queue<int> record;
bool visit[Max];
//bool visit[Max];
typedef struct Arc{ //边结点
int number;
int value;
struct Arc *next;
}ArcNode;
typedef struct{ //表头结点
type_data data;
ArcNode *first;
}VertexNode;
typedef struct{ //图
int n;//结点数
int m;//边数
VertexNode tex[Max];
}AdjList;
void Creat_grap(AdjList *g){
scanf("%d%d",&g->n,&g->m);
memset(coun,0,sizeof(coun));
int i,s,t,val;
for(i=1;i<=g->n;i++){
scanf("%d",&g->tex[i].data);
g->tex[i].first=NULL;
}
for(i=0;i<g->m;i++){
scanf("%d%d%d",&s,&t,&val);
coun[s]=true;
ArcNode *p=(ArcNode*)malloc(sizeof(ArcNode));
p->number=t;
p->value=val;
p->next=g->tex[s].first;
g->tex[s].first=p;
/* ArcNode *q=(ArcNode*)malloc(sizeof(ArcNode)); //当为有向图时不能写
q->number=s;
q->value=val;
q->next=g->tex[t].first;
g->tex[t].first=q;*/
}
}
void Clear(AdjList *g){
for(int i=1;i<=g->n;i++){
ArcNode *p=g->tex[i].first;
while(p){
ArcNode *q=p->next;
free(p);
p=q;
}
}
}
void dfs(AdjList *g,int num){
visit[num]=true;
for(int i=1;i<=g->n;i++){
if(!i:adj[num] && !visit[i])
dfs(g,q->number);
}
record.push(num);
}
void Insert_Edfs(AdjList *g){
queue<int> que;
memset(visit,0,sizeof(visit));
for(int i=1;i<=g->n;i++)
if(!coun[i])
que.push(i);
while(!que.empty()){
int temp=que.front();
que.pop();
if(!visit[temp])
dfs(g,temp);
}
}
int main(){
AdjList g;
Creat_grap(&g);
Insert_Edfs(&g);
Clear(&g);
return 0;
}