拓扑排序是对有向图进行排序的一种算法,它可以得到一个顶点的线性序列,使得对于图中的任意一条有向边 (u, v),顶点 u 在序列中都出现在顶点 v 的前面。换句话说,如果存在一条有向边 (u, v),那么在排序后的序列中,顶点 u 出现在顶点 v 的前面。
主要用于解决有向无环图(DAG)相关的问题(但不限于有向无环图),比如任务调度、依赖关系分析等。通过拓扑排序,我们可以确定一组任务的执行顺序,或者确定一组任务之间的执行依赖关系。
特别注意的是,拓扑排序并不适用于无向图,因为无向图不存在顶点的指向性,而且很可能存在环路,因此无法进行拓扑排序。
//有向无环图的拓扑排序
#include
#include
typedef struct graph
{
char* vexs;//顶点数值
int** arcs;//邻接矩阵
int vexNum;//顶点数
int arcNum;//边数
}Graph;
typedef struct Node//栈的建立与存顶点下标有关
{
int data;
struct Node* next;
}Node;
Node* initStack()
{
Node* stack=(Node*)malloc(sizeof(Node));
stack->data=0;
stack->next=NULL;
return stack;
}
void push(Node* stack,int data)//压栈
{
Node* node=(Node*)malloc(sizeof(Node));
node->data=data;
node->next=stack->next;
stack->next=node;
stack->data++;
}
int isEmpty(Node* stack)//判断是否为空栈
{
if(stack->next==NULL)
{
return 1;
}
else
{
return 0;
}
}
int pop(Node* stack)//出栈
{
if(isEmpty(stack))
{
return -1;
}
else
{
Node* node=stack->next;
int data=node->data;
stack->next=node->next;
free(node);
stack->data--;
return data;
}
}
Graph* initGraph(int vexNum)//分配空间
{
Graph* G = (Graph*)malloc(sizeof(Graph));
G -> vexs = (char*)malloc(sizeof(char) * vexNum);
G -> arcs = (int**)malloc(sizeof(int*) * vexNum);
for (int i = 0 ; i < vexNum; i++)
{
G -> arcs[i] = (int*)malloc(sizeof(int) * vexNum);
}
G -> vexNum = vexNum;
G -> arcNum = 0;
return G;
}
void createGraph(Graph* G, char* vexs, int* arcs)//创建图
{
for (int i = 0 ; i < G -> vexNum; i++)
{
G -> vexs[i] = vexs[i];
for (int j = 0; j < G -> vexNum; j++)
{
G -> arcs[i][j] = *(arcs + i * G -> vexNum + j);
if (G -> arcs[i][j] != 0)
G -> arcNum ++;
}
}
G -> arcNum /= 2;
}
int* findInDegrees(Graph* G)//找出入度
{
int* inDegrees=(int*)malloc(sizeof(int)*G->vexNum);
for(int i=0;ivexNum;i++)//初始化
{
inDegrees[i]=0;
}
for(int i=0;ivexNum;i++)
{
for(int j=0;jvexNum;j++)
{
if(G->arcs[i][j])
{
inDegrees[j]++;
}
}
}
return inDegrees;
}
void topologicalSort(Graph* G)//拓扑排序
{
int index=0;
int* top=(int*)malloc(sizeof(int)*G->vexNum);//存下标的数组
int* inDegrees=findInDegrees(G);
Node* stack=initStack();
for(int i=0;ivexNum;i++)//入度为0的压栈
{
if(inDegrees[i]==0)
{
push(stack,i);
}
}
while(!isEmpty(stack))//栈不为空,循环执行入度的减法(去掉输出顶点指向的下一个顶点的边)
{
int vexindex=pop(stack);//出栈的是顶点的下标
top[index++]=vexindex;//保存顶点下标
for(int j=0;jvexNum;j++)
{
if(G->arcs[vexindex][j])//下一个顶点有入度时减去
{
inDegrees[j]--;
if(inDegrees[j]==0)//顶点入度减到0了直接入栈
{
push(stack,j);
}
}
}
}
for(int i=0;ivexNum;i++)//依次输出入度为零的顶点
{
printf("%c ",G->vexs[top[i]]);
}
printf("\n");
}
void DFS(Graph* G,int* flag,int index)//深度优先遍历
{
printf("%c ",G->vexs[index]);
flag[index]=1;//已经访问过顶点标记为1,之后不会再访问
for(int i=0;ivexNum;i++)
{
if(G->arcs[index][i]==1&&!flag[i])
{
DFS(G,flag,i);
}
}
}
int main()
{
Graph* G=initGraph(6);
int* flag=(int*)malloc(sizeof(int)*G->vexNum);
for(int i=0;ivexNum;i++)//首先赋值为0,表示未访问任何顶点
{
flag[i]=0;
}
int arcs[6][6]={
0,1,1,1,0,0,
0,0,0,0,0,0,
0,1,0,0,1,0,
0,0,0,0,1,0,
0,0,0,0,0,0,
0,0,0,1,1,0
};
createGraph(G,"123456",(int*)arcs);
DFS(G,flag,0);
printf("\n");
topologicalSort(G);
return 0;
}
运行结果:
1 2 3 5 4
6 1 4 3 5 2
(空行)
答:在拓扑排序中,首先找到入度为 0 的顶点(即没有任何边指向该顶点),将其压入栈中。然后不断地从栈中弹出顶点,并将与之相连的顶点的入度减 1。如果某个顶点的入度减为 0,则将其压入栈中。重复这个过程直到栈为空,最终栈中顶点的出栈顺序就是拓扑排序的结果。
因此,使用栈可以方便地实现拓扑排序的过程,对算法的实现和理解都非常有帮助。
答:可以用来判断图中各个顶点连通性。通过遍历过程中访问到的顶点,可以判断哪些顶点是连通的,哪些是孤立的。
因此依据以上代码运行的结果来看,要是从1顶点开始遍历,最后无法输出6顶点,那么6顶点在有向无环图中是孤立顶点,无法输出。(反之要是从6顶点开始遍历,最后1顶点无法输出,1也可以叫孤立顶点)
NO.34