数据结构之图的应用(三)之拓扑排序

前面的两期文章讲的两种图的应用——最小生成树和最短路径都是根据这个无向图的讨论的。而今天应用主要是放在有向无环图中讨论的。接下来,我们一起来看看这种拓扑排序吧

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;
}

好啦,关于这拓扑排序的内容就到这啦。大家没事可以理解理解。
本贴为博主亲手整理。如有错误,请评论区指出,一起进步。谢谢大家的浏览.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值