美文网首页
图的拓扑排序算法

图的拓扑排序算法

作者: lkmc2 | 来源:发表于2018-08-20 10:38 被阅读12次

拓扑排序:对一个有向图构造拓扑序列的过程。

关键概念定义

拓扑序列:设G=(V,E)是一个具有n个顶点的有向图,V中的顶点序列v1,v2,……,vn,若满足从顶点vi到vj有一条路径,则在顶点序列中顶点vi必须出现在vj之前。符合这样定义的序列叫拓扑序列。

AOV网:在一个表示工程的有向图中,用顶点表示活动,用弧表示活动之间的优先关系,这样的有向图为顶点表示活动的网,就是AOV网。

拓扑排序算法思路:从AOV网中选择一个入度为0的顶点输出,然后删除此顶点,并删除此顶点为尾的弧,重复此步骤,直到输出全部顶点或者AOV网中不存在入度为0的顶点为止。

// 图的拓扑排序算法
#include <stdio.h>
#include <malloc.h>

#define MAXEDGE 20 // 最大边数
#define MAXVEX 14 // 最大顶点数

#define OK 1 // 执行成功
#define ERROR 0 // 执行失败

typedef int Status; // 执行状态

// 邻接矩阵结构
typedef struct {
    int vexs[MAXVEX]; // 顶点表
    int arc[MAXVEX][MAXVEX]; // 边表
    int numNodes, numEdges; // 图中当前的顶点数,边数
} MGraph;

/**************** 用到的邻接表结构 ****************/

// 边表节点
typedef struct EdgeNode {
    int adjvex; // 邻接点域,存储该顶点对应的下标
    int weight; // 用来存储权值(非网图可没有)
    struct EdgeNode *next; // 指向下一个邻接点
} EdgeNode;

// 顶点表节点
typedef struct VertexNode {
    int in; // 顶点入度
    int data; // 顶点域,存储顶点信息
    EdgeNode *firstEdge; // 边表头指针
} VertexNode, AdjList[MAXVEX];

// 邻接表结构
typedef struct {
    AdjList adjList;// 顶点表数组
    int numNodes, numEdges; // 图中当前顶点数,边数
} graphAdjList, *GraphAdjList;

/*************************************************/

/**
 * 生成邻接矩阵(图)
 * @param G 邻接矩阵(图)
 */
void CreateMGraph(MGraph *G) {
    int i, j; // 用于遍历元素

    G->numEdges = MAXEDGE; // 设置边数
    G->numNodes = MAXVEX; // 设置顶点数

    // 初始化图的顶点表
    for (i = 0; i < G->numNodes; i++) {
        G->vexs[i] = i;
    }

    // 初始化图的边表
    for (i = 0; i < G->numNodes; i++) {
        for (j = 0; j < G->numNodes; j++) {
            G->arc[i][j] = 0; // 设置所有边的默认值为0
        }
    }

    // 设置特定边的权(值为1表示该位置有边)
    G->arc[0][4] = 1;
    G->arc[0][5] = 1;
    G->arc[0][11] = 1;

    G->arc[1][2] = 1;
    G->arc[1][4] = 1;
    G->arc[1][8] = 1;

    G->arc[2][5] = 1;
    G->arc[2][6] = 1;
    G->arc[2][9] = 1;

    G->arc[3][2] = 1;
    G->arc[3][13] = 1;

    G->arc[4][7] = 1;

    G->arc[5][8] = 1;
    G->arc[5][12] = 1;

    G->arc[6][5] = 1;

    G->arc[8][7] = 1;

    G->arc[9][10] = 1;
    G->arc[9][11] = 1;

    G->arc[10][13] = 1;

    G->arc[12][9] = 1;
}

/**
 * 使用邻接矩阵生成邻接表
 * @param G 邻接矩阵
 * @param GL 邻接表
 */
void CreateALGraph(MGraph G, GraphAdjList *GL) {
    int i, j; // 用于遍历元素
    EdgeNode *e; // 边表节点

    *GL = (GraphAdjList) malloc(sizeof(graphAdjList)); // 为邻接表表分配存储空间

    (*GL)->numNodes = G.numNodes; // 设置邻接表的顶点数
    (*GL)->numEdges = G.numEdges; // 设置邻接表的边数

    // 建立顶点表
    for (i = 0; i < G.numNodes; i++) {
        (*GL)->adjList[i].in = 0; // 设置下标为i位置的顶点入读为0
        (*GL)->adjList[i].data = G.vexs[i]; // 存入该顶点的权值
        (*GL)->adjList[i].firstEdge = NULL; // 设置顶点对应的边表为空
    }

    // 建立边表
    for (i = 0; i < G.numNodes; i++) {
        for (j = 0; j < G.numNodes; j++) {
            if (G.arc[i][j] == 1) {
                e = (EdgeNode *) malloc(sizeof(EdgeNode)); // 为边表节点分配存储空间
                e->adjvex = j; // 设置邻接序号为j
                e->next = (*GL)->adjList[i].firstEdge; // 新节点指针指向边表的头节点
                (*GL)->adjList[i].firstEdge = e; // 让新节点成为边表的头节点
                (*GL)->adjList[j].in++; // 边终点所指向的节点入度加1
            }
        }
    }
}

/**
 * 拓扑排序
 * 若邻接表无回路,则输出拓扑排序序列并返回OK,若有回路则返回ERROR
 * @param GL 邻接表
 * @return 执行状态
 */
Status TopologicalSort(GraphAdjList GL) {
    EdgeNode *e; // 边表节点
    int i, k, gettop; // i用于遍历元素,k用来获取顶点下标,gettop用来获取栈顶元素下标
    int top = 0; // 用于栈指针下标
    int count = 0; // 用于统计输出顶点的个数
    int *stack; // 存储入度为0的顶点的栈

    stack = (int *) malloc(GL->numNodes * sizeof(int)); // 为栈分配存储空间

    // 将所有入度为0的顶点入栈
    for (i = 0; i < GL->numNodes; i++) {
        if (GL->adjList[i].in == 0) { // 顶点入度为0
            stack[++top] = i; // 将该节点入栈
        }
    }

    // 未把所有入度为0的顶点出栈
    while (top != 0) {
        gettop = stack[top--]; // 获取入度为0的栈的栈顶元素
        printf("%d -> ", GL->adjList[gettop].data); // 打印该元素的顶点信息
        count++; // 输入顶点个数加1

        // 遍历该顶点的边表
        for (e = GL->adjList[gettop].firstEdge; e; e = e->next) {
            k = e->adjvex; // 获取下个顶点(边终点所指向的节点)的下标

            // 将边终点所指向的节点的入度减1,如果该顶点入读为0,将顶点入栈
            if (!(--GL->adjList[k].in)) {
                stack[++top] = k; // 将顶点入栈
            }
        }
    }
    printf("\n");

    // 输出的顶点数小于邻接表的顶点数,邻接表中有回路,拓扑排序失败
    if (count < GL->numNodes) {
        return ERROR;
    } else { // 邻接表中无回路,拓扑排序成功
        return OK;
    }
}

int main() {
    MGraph G; // 邻接矩阵
    GraphAdjList GL; // 邻接表
    Status status; // 执行状态

    CreateMGraph(&G); // 创建邻接矩阵
    CreateALGraph(G, &GL); // 使用邻接矩阵创建邻接表

    status = TopologicalSort(GL); // 将邻接表进行拓扑排序,获取执行状态
    printf("status: %s", status == OK ? "执行成功" : "执行失败");

    return 0;
}
运行结果

相关文章

  • 7.6图的应用:拓扑排序

    拓扑排序Topological Sort ❖从工作流程图得到工作次序排列的算法,称为“拓扑排序”❖拓扑排序处理一个...

  • 18-拓扑排序

    拓扑排序## 拓扑排序是针对有向无环图定义的,此算法可以判断一个有向图是否存在回路。拓扑排序反应的是活动和工程的先...

  • Leetcode --- 课程表问题(拓扑排序)

    写在前:拓扑排序本质是BFS和贪心算法,是这两种算法在有向图应用的专有名词,即拓扑排序针对有向图问题。参考这里[h...

  • 图的最短路径和拓扑排序

    拓扑排序 2、图的最短路径 3、图的拓扑排序

  • 通用的深度优先搜索+图的应用1:拓扑排序

    问题背景: 工作流程图到工作次序的排序的算法,称为拓扑排序算法 问题解决思路: 将工作次序变为图,图的顶点代表每项...

  • 强连通分量和Kosaraju算法

    内容概要: 基于深度优先后序遍历的DAG图拓扑排序 强连通分量 求解强连通分量Kosaraju算法 拓扑排序的另一...

  • LeetCode 第207题:课程表

    1、前言 2、思路 使用拓扑排序的方法,拓扑排序其实是使用的 BFS 算法,简而言之使用 BFS 算法解题。算法流...

  • 图的拓扑排序算法

    拓扑排序:对一个有向图构造拓扑序列的过程。 关键概念定义 拓扑序列:设G=(V,E)是一个具有n个顶点的有向图,V...

  • 模板

    并查集 拓扑排序 Floyd算法 Dijkstra算法

  • Graph-一般算法

    和图相关的算法有:最小生成子树,最短路径,拓扑排序。 这里仅介绍最小生成树和最短路径,拓扑排序暂时省略。 最小生成...

网友评论

      本文标题:图的拓扑排序算法

      本文链接:https://www.haomeiwen.com/subject/pmskiftx.html