本文已参与「新人创作礼」活动,一起开启掘金创作之路。
算法步骤
- 遍历所有图顶点, 把入度为0的顶点入队
- 当队列不为空时, 取出一个顶点v输出
- 将与v相邻的顶点入度减1, 然后把减1后入度为0的顶点入队
- 重复2, 3步, 直到队列为空, 算法结束
代码实现
#include <iostream>
#include <algorithm>
#include <cstdlib>
using namespace std;
const int MaxV = 100;
/*定义队列*/
typedef struct QNode{
int *Data;
int Front, Rear;
int MaxSize;
} * Queue;
/*定义边*/
typedef struct ENode{
int v1, v2;
int Weight;
} * Edge;
/*定义邻接结点*/
struct AdjVNode{
int AdjV;
int Weight;
AdjVNode *Next;
};
/*定义邻接表头*/
typedef struct VNode{
AdjVNode *EdgeFirst;
//bool tag; //标记是否被访问, 该题不需要此标记, 入度为0后, 邻接表就不会在访问它了
int Indegree; //该结点的入度数
} AdjList[MaxV];
/*定义图*/
typedef struct GNode{
int Nv, Ne;
AdjList L;
} * LGraph;
/*创建队列并初始化*/
Queue CreateQueue(int MaxSize) //给定队列最大长度
{
Queue Q = (Queue)calloc(1, sizeof(QNode));
Q->Data = (int *)calloc(MaxSize, sizeof(int));
Q->MaxSize = MaxSize;
return Q;
}
/*入队操作*/
void AddQ(Queue Q, int x)
{
Q->Rear = (Q->Rear + 1) % Q->MaxSize;
Q->Data[Q->Rear] = x;
}
/*出队操作*/
int DeleteQ(Queue Q)
{
Q->Front = (Q->Front + 1) % Q->MaxSize;
return Q->Data[Q->Front];
}
/*
队列空: Q->Front == Q->Rear;
队列满: (Q->Rear + 1) % Q->MaxSize == Q->Front;
*/
/*创造有Nv各结点的无边图*/
LGraph CreateGraph(int Nv)
{
LGraph G = (LGraph)calloc(1, sizeof(GNode));
G->Nv = Nv;
return G;
}
/*给图插入边*/
void InsertEdge(LGraph G, Edge E)
{
AdjVNode *A = (AdjVNode *)malloc(sizeof(AdjVNode));
A->AdjV = E->v2;
A->Next = G->L[E->v1].EdgeFirst;
G->L[E->v1].EdgeFirst = A;
}
/*创建图表*/
LGraph BuildGraph()
{
int Nv; cin >> Nv;
LGraph G = CreateGraph(Nv);
cin >> G->Ne;
Edge E = (Edge)malloc(sizeof(ENode));
for (int i = 0; i < G->Ne; i++)
{
cin >> E->v1 >> E->v2;
InsertEdge(G, E);
G->L[E->v2].Indegree++; //计算入度
}
free(E);
return G;
}
/*拓扑排序*/
bool TopSort(LGraph G, int TopOrder[])
{
Queue Q = CreateQueue(G->Nv);
for (int i = 0; i < G->Nv; i++)
if(G->L[i].Indegree == 0)
AddQ(Q, i);
int cnt = 0;
AdjVNode *A;
while (Q->Front != Q->Rear) //如果队列非空
{
int v = DeleteQ(Q);
TopOrder[cnt++] = v;
A = G->L[v].EdgeFirst;
while (A)
{
if (--G->L[A->AdjV].Indegree == 0) //如果入度减1为0
AddQ(Q, A->AdjV);
A = A->Next;
}
}
/*如果队列空以后*/
free(Q->Data);
free(Q);
if(cnt != G->Nv)
return false; //说明有回路, 有回路就会有一些点入度不可能降为0
else
return true;
}
/*删除图, 释放图占用的储存空间*/
void DeleteGraph(LGraph G)
{
AdjVNode *A;
for (int i = 0; i < G->Nv; i++)
while (G->L[i].EdgeFirst)
{
A = G->L[i].EdgeFirst;
G->L[i].EdgeFirst = A->Next;
free(A);
}
free(G);
}
int main()
{
int TopOrder[MaxV]; //储存拓扑排序
LGraph G = BuildGraph();
if(!TopSort(G,TopOrder))
cout << "有回路, 无拓扑排序" << endl;
else
{
cout << "拓扑排序为:" << endl;
for (int i = 0; i < G->Nv; i++)
cout << TopOrder[i] << " ";
cout << endl;
}
DeleteGraph(G);
system("pause");
return 0;
}
输入实例
8 10
0 1
0 2
1 2
3 4
4 2
2 7
2 6
4 5
5 6
6 7
如果加一条边<6, 4>使图有回路
8 11
0 1
0 2
1 2
3 4
4 2
2 7
2 6
4 5
5 6
6 7
6 4