数据结构编程笔记二十三:第七章 图 关键路径算法的实现

上次我们介绍了图的拓扑排序算法的实现,这次介绍基于邻接表的关键路径算法的实现。还是老规矩:程序在码云上可以下载。 地址:https://git.oschina.net/601345138/DataStructureCLanguage.git本次关键路径程序共用到以下源文件,有一些已经在之前的文章介绍过。还是和以前一样,所有源文件需要放在同一目录下编译。 my_constants.h 各种状态
摘要由CSDN通过智能技术生成

上次我们介绍了图的拓扑排序算法的实现,这次介绍基于邻接表的关键路径算法的实现。

还是老规矩:

程序在码云上可以下载。
地址:https://git.oschina.net/601345138/DataStructureCLanguage.git

本次关键路径程序共用到以下源文件,有一些已经在之前的文章介绍过。还是和以前一样,所有源文件需要放在同一目录下编译
my_constants.h 各种状态码定义
LinkedList.cpp 不带头结点单链表各种基本操作的实现
ALGraph.h 图的邻接表存储结构表示定义
ALGraph.cpp 基于邻接表的图的基本操作实现
Stack.cpp 顺序栈的基本操作实现
CriticalPath.cpp 关键路径算法的实现
关键路径测试.cpp 主函数,调用算法完成关键路径程序的演示

为了操作实现方便,我使用了单链表程序来简化邻接表部分操作,但是这个单链表与《 数据结构编程笔记四:第二章 线性表 单链表的实现》一文介绍的单链表有所区别,本文用到的单链表不带头结点,很多操作与带头结点的单链表有区别。望读者注意。具体程序参见《数据结构编程笔记十九:第七章 图 图的邻接表存储表示及各基本操作的实现》

邻接表在《数据结构编程笔记十九:第七章 图 图的邻接表存储表示及各基本操作的实现》一文有所介绍,my_constants.h、ALGraph.h 、ALGraph.cpp和LinkedList.cpp四个源文件与此文中的同名源文件内容完全一致,没有修改。这里不再重复贴了(否则文章会很长,不能突出重点),但在码云上你可以下载到全部源文件,我会把它放在一个目录下。

关键路径操作用到了拓扑排序的算法,如果不清楚拓扑排序过程的读者请参考《数据结构编程笔记二十二:第七章 图 拓扑排序算法的实现》。本次程序用到拓扑排序程序中的一个求顶点入度的函数。

拓扑排序操作在实现过程中使用了栈。我引入了已经实现的顺序栈来辅助完成拓扑排序。
顺序栈在《 数据结构编程笔记八:第三章 栈和队列 顺序栈和进位制程序的实现》一文中已经有所介绍,除了修改栈的数据类型为int,源代码其余部分我都没有改变。需要的读者可以参考《数据结构编程笔记二十二:第七章 图 拓扑排序算法的实现》这篇文章。我不再贴出源码。

任何一项工程都有若干事件和活动,这些事件和活动连接起来就是整个工程的流程。假设你是一名项目工程师,你所要做的工作就是设法加快项目进度,节约时间(软件工程中项目经理这个角色就得操心项目进度规划的事),工期每拖一天就要多支付工人一天的报酬,而且软件工程中的项目的进度不可能计划的那么死(恰到好处,一点不留余地),总要留点余地(缓冲时间),因为万一如果项目在进行过程中因为某种意外原因拖延了项目进度,就有可能导致项目在截止日期之前完不成,一旦项目超期,甲方(项目实施方,也就是你这一方)是要付违约金的(做项目没人想要赔钱)。

工程中的有的活动有先后顺序的制约,某些活动必须等其他活动结束后方可开始(串行)而有些则可以和其他活动一同进行(并行)。这些事件和活动会组成一张图(有向网)。软件工程项目中,事件相当于里程碑(标志整个工程进入某个阶段的重要结点),也就是图中的顶点;活动相当于每一项具体的工作,也就是图中的边。

在工程进度安排中,绝对不允许出现环路。原因是显而易见的:出现环路意味着一旦进入环路将会无法跳出,项目也就无法正常结束。这是不允许的。如果有项目经理安排了这样的进度。。。(显然不可能有人会这么做)

关键路径就是用于解决工程安排的问题,它的主要目的是找出整个工程流程中的关键流程,然后工程师会尝试设法缩短关键流程的时间,这样就可以起到加快整个工程进度,节约时间的目的。

计算关键路径之前,我们首先要保证图中没有环,所以自然就想到了拓扑排序。使用拓扑排序作为执行关键路径算法的先决条件,一旦拓扑排序不成功,说明图中必有环,那就没必要继续计算关键路径了。

本次只贴关键路径的核心代码和主函数:
源文件:CriticalPath.cpp

//打印关键路径用到的几个函数 
void printhead(ALGraph G);
void printi(ALGraph G);
void printvex(ALGraph G);
void printve(ALGraph G, int ve[]);
void printvl(ALGraph G, int vl[]);
void printCritical1(ALGraph G, int ve[], int vl[]);

/*
    函数:FindInDegree
    参数:ALGraph G 图G(邻接表存储结构) 
          int indegree[] 存储顶点入度的数组 
    返回值:无
    作用:求顶点的入度
*/
void FindInDegree(ALGraph G, int indegree[]) {

    //工作指针p 
    ArcNode *p;

    //对存储入度的indegree数组赋初值0
    for(int i = 0; i < MAX_VERTEX_NUM; i++) {
        indegree[i] = 0; 
    }//for

    //扫描每个顶点后面的弧链表 
    for(int i = 0; i < G.vexnum; i++) {

        //p指向顶点i后面弧链表的首元结点 
        p = G.vertices[i].firstarc;

        //顶点v的弧链表没有扫描完毕 
        while(p) { //while(p) <=> while(p != NULL) 
            //每找到一个弧结点,对应邻接点的入度+1 
            indegree[p->data.adjvex]++;

            //p指向下一个弧结点 
            p = p->nextarc;
        }//while 
    }//for
}//FindInDegree


//事件最早发生时间,全局变量
int ve[MAX_VERTEX_NUM];

/*
    函数:TopologicalOrder
    参数:ALGraph G 图的邻接表存储表示
          Stack &T 拓扑序列顶点栈  
    返回值:状态码,OK表示操作成功,ERROR表示操作失败 
    作用:有向网G采用邻接表存储结构,求各顶点事件的最早发生时间ve(全局变量)。
          若G无回路,则用栈T返回G的一个拓扑序列,且函数值为OK,否则为ERROR     
*/
Status TopologicalOrder(ALGraph G, Stack &T) {

    //count是已入栈顶点数,初值为0
    int i = 0, k = 0, count = 0;

    //入度数组,存放各顶点当前入度数
    int indegree[MAX_VERTEX_NUM];

    //S为零入度顶点栈。 
    Stack S;

    //指向弧结点的工作指针p 
    ArcNode *p;

    //对各顶点求入度并存入数组indegree[]
    FindInDegree(G, indegree);

    //初始化零入度顶点栈S
    InitStack(S);

    printf("->求得的拓扑序列:");

    //对所有顶点i
    for(i = 0; i < G.vexnum; ++i) { 

        //若其入度为0,将i压入零入度顶点栈S
        //if(!indegree[i]) <=> if(indegree[i] == 0)
        if(!indegree[i]) {
            Push(S, i);
        }//if
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值