前面的两期文章讲的两种图的应用——最小生成树和最短路径都是根据这个无向图的讨论的。而今天应用主要是放在有向无环图中讨论的。接下来,我们一起来看看这种拓扑排序吧
1.有向无环图
总的来说,就是没有像有向图一样全部连接起来成环的图。
好啦,扩展完毕后,接下来,我们就正式来看看拓扑排序吧。
核心思路:
原理图:
拓扑排序没有唯一的结果,可以有很多种结果哦。
因为有些顶点找不到前驱顶点,如图的构成环的三个顶点。
实现拓扑排序代码:
#include <stdio.h>
#include <stdlib.h>
#define MAX_VERTEX_NUM 20//最大顶点个数
#define VertexType int//顶点数据的类型
typedef struct ArcNode {
int adjvex;//邻接点在数组中的位置下标
struct ArcNode* nextarc;//指向下一个邻接点的指针
}ArcNode;
typedef struct VNode {
VertexType data;//顶点的数据域
ArcNode* firstarc;//指向邻接点的指针
}VNode, AdjList[MAX_VERTEX_NUM];//存储各链表头结点的数组
typedef struct {
AdjList vertices;//图中顶点及各邻接点数组
int vexnum, arcnum;//记录图中顶点数和边或弧数
}ALGraph;
//找到顶点对应在邻接表数组中的位置下标
int LocateVex(ALGraph G, VertexType u) {
for (int i = 0; i < G.vexnum; i++) {
if (G.vertices[i].data == u) {
return i;
}
}return -1;
}
//创建 AOV 网,构建邻接表
void CreateAOV(ALGraph** G) {
*G = (ALGraph*)malloc(sizeof(ALGraph));
scanf_s("%d,%d", &((*G)->vexnum), &((*G)->arcnum));
for (int i = 0; i < (*G)->vexnum; i++) {
scanf_s("%d", &((*G)->vertices[i].data));
(*G)->vertices[i].firstarc = NULL;
}
VertexType initial, end;
for (int i = 0; i < (*G)->arcnum; i++) {
scanf_s("%d,%d", &initial, &end);
ArcNode* p = (ArcNode*)malloc(sizeof(ArcNode));
p->adjvex = LocateVex(*(*G), end);
p->nextarc = NULL;
int locate = LocateVex(*(*G), initial);
p->nextarc = (*G)->vertices[locate].firstarc;
(*G)->vertices[locate].firstarc = p;
}
}
//结构体定义栈结构
typedef struct stack {
VertexType data;
struct stack* next;
}stack;
//初始化栈结构
void initStack(stack** S) {
(*S) = (stack*)malloc(sizeof(stack));
(*S)->next = NULL;
}
//判断链表是否为空
bool StackEmpty(stack S) {
if (S.next == NULL) {
return true;
}return false;
}
//进栈,以头插法将新结点插入到链表中
void push(stack* S, VertexType u) {
stack* p = (stack*)malloc(sizeof(stack));
p->data = u;
p->next = NULL;
p->next = S->next;
S->next = p;
}
//弹栈函数,删除链表首元结点的同时,释放该空间,并将该结点中的数据域通过地址传值给变量 i;
void pop(stack *S,VertexType *i){
stack* p = S->next;
*i = p->data;
S->next = S->next->next;
free(p);
}
//统计各顶点的入度
void FindInDegree(ALGraph G, int indegree[]) {
//初始化数组,默认初始值全部为 0
for (int i = 0; i < G.vexnum; i++) {
indegree[i] = 0;
}
//遍历邻接表,根据各链表中结点的数据域存储的各顶点位置下标,在 indegree 数组相应位置+1
for (int i = 0; i < G.vexnum; i++) {
ArcNode* p = G.vertices[i].firstarc;
while (p) {
indegree[p->adjvex]++;
p = p->nextarc;
}
}
}
void TopologicalSort(ALGraph G)
{
int indegree[G.vexnum];//创建记录各顶点入度的数组
FindInDegree(G, indegree);//统计各顶点的入度
//建立栈结构,程序中使用的是链表
stack* S;
initStack(&S);
//查找度为 0 的顶点,作为起始点
for (int i = 0; i < G.vexnum; i++) {
if (!indegree[i]) {
push(S, i);
}
}
int count = 0;
//当栈为空,说明排序完成
while (!StackEmpty(*S)) {
int index;
//弹栈,并记录栈中保存的顶点所在邻接表数组中的位置
pop(S, &index);
printf("%d", G.vertices[index].data);
++count;
//依次查找跟该顶点相链接的顶点,如果初始入度为 1,当删除前一个顶点后,该顶点入度为 0
for (ArcNode* p = G.vertices[index].firstarc; p; p = p->nextarc) {
VertexType k = p->adjvex;
if (!(--indegree[k])) {
//顶点入度为 0,入栈
push(S, k);
}
}
}
//如果 count 值小于顶点数量,表明该有向图有环
if (count < G.vexnum) {
printf("该图有回路"); return;
}
}
int main() {
ALGraph* G;
CreateAOV(&G);//创建 AOV 网
TopologicalSort(*G);//进行拓扑排序
return 0;
}
好啦,关于这拓扑排序的内容就到这啦。大家没事可以理解理解。
本贴为博主亲手整理。如有错误,请评论区指出,一起进步。谢谢大家的浏览.