当前位置: 首页 > news >正文

数据结构应用实例(五)——关键路径

Content:

      • 一、问题描述
      • 二、算法思想
      • 三、代码实现
      • 四、小结

一、问题描述

  设计实现 AOE 网的关键活动与关键路径问题;

二、算法思想

  1. 获取拓扑序列;
  2. 计算节点的最早开始时间 v e [ i ] ve[i] ve[i]
  3. 计算节点的最晚开始时间 v l [ j ] vl[j] vl[j]
  4. 查找关键路径

三、代码实现

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define maxx 999999
#pragma warning(disable:4996)typedef struct arc//弧
{	int index;//指向节点编号int weight;//边的权值struct arc *next;
}AR;typedef struct MyGraph
{int type;//0表示无向图,1表示有向图int arcnum,vexnum;//边的个数、顶点个数char **vexname;//存放顶点名称的二维数组	AR *N;//表头数组int **A;//邻接矩阵
}GH;int findvex(char *s,GH *G);//找到图G中名称为s的节点编号,并将其返回
void createGraph(GH *G);//创建图G
void showGraph(GH *G);//以邻接表的形式显示图Gint Topological_sort(GH *G,int *q);//对G进行拓扑排序,q用于存放拓扑序列;如果G中有回路,返回1,否则返回0
void CriticalPath(GH *G);//寻找G中的关键路径					
void findCP(int *t,int top,int end,int *visit,GH *G,int *ve,int *vl,int *count);//递归函数,用于在图G中寻找关键路径;
//t为栈,存储关键路径上节点编号;end表示汇点编号;visit表示访问标记数组;ve,vl表示节点的最早开始时间和最晚开始时间;count表示关键路径条数;int main(void)
{GH *G;G=(GH *)malloc(sizeof(GH));createGraph(G);printf("图的邻接表形式:\n");showGraph(G);CriticalPath(G);free(G);return 0;
}int findvex(char *s,GH *G)//找到图G中名称为s的节点编号,并将其返回
{int i;for(i=0;i<G->vexnum;i++){if(strcmp(s,G->vexname[i])==0)return i;}printf("读取文件错误.\n");exit(-1);
}void createGraph(GH *G)//创建图G
{int i,j,n,edge;char filename[]="graph.txt";//存放图的数据文件char str[10],str1[10];FILE *fp;AR *p;fp=fopen(filename,"r");if(!fp){printf("打开文件失败!\n");exit(-1);}fscanf(fp,"%d",&G->type);//读取图的类型G->arcnum=0;fscanf(fp,"%d",&n);//读取结点数量G->vexnum=n;//为动态数组分配空间G->vexname=(char **)malloc(n*sizeof(char *));G->N=(AR *)malloc(n*sizeof(AR));G->A=(int **)malloc(n*sizeof(int *));//对头结点数组和邻接矩阵初始化for (i = 0; i < n; i++){G->N[i].next = NULL;G->A[i] = (int *)malloc(n*sizeof(int));for (j = 0; j < n; j++)G->A[i][j]=maxx;}//读取顶点名称for(i=0;i<n;i++){fscanf(fp,"%s",str);G->vexname[i]=(char *)malloc(strlen(str)*sizeof(char));strcpy(G->vexname[i],str);}//读取边while(!feof(fp)){fscanf(fp,"%s",str);fscanf(fp,"%s",str1);fscanf(fp,"%d",&edge);i=findvex(str,G);j=findvex(str1,G);//邻接表p=(AR *)malloc(sizeof(AR));p->index=j;p->weight=edge;p->next=G->N[i].next;G->N[i].next=p;//邻接矩阵G->A[i][j]=edge;G->arcnum++;//边的个数增加if(G->type==0)//如果是无向图{//邻接表p=(AR *)malloc(sizeof(AR));p->index=i;p->weight=edge;p->next=G->N[j].next;G->N[j].next=p;//邻接矩阵G->A[j][i]=edge;}}fclose(fp);
}void showGraph(GH *G)//以邻接表的形式显示图G
{int i;AR *p;//用于遍历for (i = 0; i < G->vexnum; i++){printf("%s",G->vexname[i]);p=G->N[i].next;while (p){if (G->type == 1)printf("-->");else//无向图没有箭头printf("--");printf("%s(%d)",G->vexname[p->index],p->weight);p=p->next;}printf("\n");}printf("\n");
}int Topological_sort(GH *G,int *q)//对G进行拓扑排序,q用于存放拓扑序列;如果G中有回路,返回1,否则返回0
{int i,n;int *d;int *t,top;int index,count;AR *p;n=G->vexnum;d=(int *)malloc(n*sizeof(int));//统计各个节点的入度t=(int *)malloc(n*sizeof(int));//建立栈,用于存储节点编号top=-1;//初始化,将各个节点的入度设为0for (i = 0; i < n; i++)d[i] = 0;//遍历表头数组,统计各个节点的入度for (i = 0; i < n; i++){p=G->N[i].next;while (p){d[p->index]++;p=p->next;}}//挑选入度为0的点进栈for (i = 0; i < n; i++){if (d[i] == 0){top++;t[top]=i;}}count=0;//统计弹出的节点个数while (top >= 0)//若栈非空,弹栈{index=t[top];//栈顶元素编号//栈顶元素不输出//printf("%s ",G->vexname[index]);top--;//记录弹出序列,即拓扑序列q[count]=index;count++;//遍历弹出节点的邻接表,其相邻点的入度减一p=G->N[index].next;while (p){d[p->index]--;if (d[p->index] == 0)//若入度变为0,进栈{top++;t[top]=p->index;}p=p->next;}}//printf("\n");//输出free(t);free(d);if (count == n)//拓扑序列中含有G中全部点,表示没有回路return 0;elsereturn 1;
}void CriticalPath(GH *G)//寻找G中的关键路径
{int i,n;int x,y;//计数器int length;//关键路径长度int num,count;//关键节点个数和关键路径条数AR *p;int *q,*ve,*vl;int *t;//用于在递归寻找关键路径时存储路径int *visit;//访问标记数组n=G->vexnum;q=(int *)malloc(n*sizeof(int));//拓扑序列,存储节点编号ve=(int *)malloc(n*sizeof(int));//最早开始时间vl=(int *)malloc(n*sizeof(int));//最晚开始时间if (Topological_sort(G,q))//获取拓扑序列{printf("该有向图中存在回路,故不存在关键路径.\n");return;}//1.计算最早开始时间//初始化,全设为0for (i = 0; i < n; i++)ve[i] = 0;for (i = 0; i < n; i++)//利用正向拓扑序列计算最早开始时间{x = q[i];p = G->N[x].next;//利用邻接表寻找x的直接后继while (p)//更新x的直接后继的最早开始时间{if (ve[p->index] < ve[x] + (p->weight))ve[p->index] = ve[x] + (p->weight);p = p->next;}}//2.计算最晚开始时间length = ve[q[n - 1]];//关键路径长度//初始化for (i = 0; i < n; i++)vl[i] = length;for (i = n - 1; i >= 0; i--)//利用逆向拓扑序列计算最晚开始时间{y = q[i];//利用邻接矩阵寻找y的直接前驱for (x = 0; x < n; x++)//更新y的直接前驱的最晚开始时间{if (G->A[x][y] < maxx)//找到之后{if (vl[x] > vl[y] - G->A[x][y])vl[x] = vl[y] - G->A[x][y];}}}//3.输出最早开始时间和最晚开始时间num=0;visit=(int *)malloc(n*sizeof(int));printf("节点名称 最早开始时间 最晚开始时间\n");for (i = 0; i < n; i++){x = q[i];//节点编号printf("%4s %10d %12d\n",G->vexname[x],ve[x],vl[x]);if (ve[x] == vl[x]){visit[x] = 0;//关键节点设置为未访问num++;}elsevisit[x]=1;//事先标记非关键节点,避免后续访问}//4.利用递归在关键节点中探寻关键路径//初始化t = (int *)malloc(num*sizeof(int));//存储关键路径中节点编号visit[q[0]]=1;t[0] = q[0];count = 0;//关键路径条数//调用递归函数findCP(t,0,q[n-1],visit,G,ve,vl,&count);printf("\n");free(ve);free(vl);free(q);	free(t);free(visit);
}//t为栈,存储关键路径上节点编号;end表示汇点编号;visit表示访问标记数组;ve,vl表示节点的最早开始时间和最晚开始时间;count表示关键路径条数;
void findCP(int *t,int top,int end,int *visit,GH *G,int *ve,int *vl,int *count)//递归函数,用于在图G中寻找关键路径;
{int i;int cur;AR *p;cur=t[top];if (cur == end)//基准情况:到达汇点,输出路径{(*count)++;	printf("\n第%d条关键路径:\n",(*count));for (i = 0; i < top; i++)printf("%s-->",G->vexname[t[i]]);		printf("%s\n",G->vexname[cur]);}else//非基准情况{p=G->N[cur].next;while (p)//遍历当前节点的直接后继{if (visit[p->index]==0 && ve[cur] + (p->weight) == vl[p->index])//关键工序(边)的判别条件,非关键节点的visit[i]==1{visit[p->index]=1;t[top+1]=p->index;//入栈findCP(t,top+1,end,visit,G,ve,vl,count);//调用递归函数visit[p->index]=0;//撤销标记}p=p->next;}}
}

四、小结

1、 为了利用拓扑序列计算最早和最迟开始时间,在进行拓扑排序时,对排序序列进行记录;
2、 对于关键路径不唯一的情况,采用 DFS 寻找关键路径,查找路径之前,标记非关键节点,避免后续访问,从而简化了节点添入路径的条件,提高算法的执行效率;
3、 t 用于存储关键路径中的节点编号,由于关键路径中的节点均是关键节点,所以在给 t 分配空间时,分配的空间单位数与关键节点个数相同即可;
4、 在实际计算最早开始时间时,做法并不与算法思想完全一致,具体做法为:先设初值, v e [ i ] ve[i] ve[i] 全部为0;然后遍历正向拓扑序列 q q q,对于编号为 q [ i ] q[i] q[i] 的节点,更新其直接后继 y y y 号节点的最早开始时间,即若 v e [ y ] < v e [ q [ i ] ] + A [ q [ i ] ] [ y ] ve[y]<ve[q[i]]+A[q[i]][y] ve[y]<ve[q[i]]+A[q[i]][y],则令 v e [ y ] = v e [ q [ i ] ] + A [ q [ i ] ] [ y ] ve[y]= ve[q[i]]+ A[q[i]][y] ve[y]=ve[q[i]]+A[q[i]][y];对于最迟开始时间,进行类似操作;

相关文章:

数据结构应用实例(五)——关键路径

Content: 一、问题描述二、算法思想三、代码实现四、小结 一、问题描述 设计实现 AOE 网的关键活动与关键路径问题&#xff1b; 二、算法思想 获取拓扑序列&#xff1b;计算节点的最早开始时间 v e [ i ] ve[i] ve[i]&#xff1b;计算节点的最晚开始时间 v l [ j ] vl[j] v…...

组播 2024 9 11

PIM&#xff08;Protocol Independent Multicast&#xff09;是一种常用的组播路由协议&#xff0c;其独立于底层的单播路由协议&#xff0c;能够在多种网络环境中有效地实现多播路由功能。PIM主要有两种模式&#xff1a;PIM Sparse Mode (PIM-SM) 和 PIM Dense Mode (PIM-DM)&…...

揭秘开发者的效率倍增器:编程工具的选择与应用

文章目录 每日一句正能量前言工具介绍功能特点&#xff1a;使用场景&#xff1a;提高工作效率的方式&#xff1a; 效率对比未来趋势后记 每日一句正能量 这推开心窗之人&#xff0c;可以是亲朋好友&#xff0c;也可以是陌客路人&#xff0c;可以是德高望重的哲人名流&#xff0…...

在AI的时代,程序员如何才不被淘汰

随着人工智能技术的迅猛发展&#xff0c;大模型&#xff08;Large Language Models, LLMs&#xff09;正逐渐成为IT行业的热点。对于程序员来说&#xff0c;转行大模型领域不仅意味着新的机遇&#xff0c;也面临着诸多挑战。本文将探讨程序员转行大模型的机遇与挑战&#xff0c…...

tabBar设置底部菜单选项以及iconfont图标,setTabBar设置TabBar和下拉刷新API

tabBartabBar属性:设置底部 tab 的表现 ​ ​ ​ ​ 首先在pages.json页面写一个tabBar对象,里面放入list对象数组,里面至少要有2个、最多5个 tab, 如果只有一个tab的话,H5(浏览器)依然可以显示底部有一个导航栏,如果没有,需要重启后才有,小程序则报错,只有2个以上才可以…...

2024C题prompt

题目 我正在进行数学建模&#xff0c;需要你为我提供帮助。下面我会将赛题背景和问题发送给你&#xff0c;请你为我提供比赛思路和指导。 以下是赛题背景和赛题说明&#xff0c;不是问题: 农作物的种植策略 根据乡村的实际情况&#xff0c;充分利用有限的耕地资源&#xff0c…...

Numpy中数组的形状处理

目录 将多维数组降为一维数组竖直方向或水平方向数组的堆叠 数组形状处理的手段主要有reshape&#xff0c;resize&#xff0c;ravel&#xff0c;flatten&#xff0c;vstack&#xff0c;hstack&#xff0c;row_stack&#xff0c;column_stack,下面通过简单 的案例来解释这些方法…...

【动态规划】子序列问题二(数组中不连续的一段)

子序列问题二 1.最长定差子序列2.最长的斐波那契子序列的长度3.最长等差数列4.等差数列划分 II - 子序列 点赞&#x1f44d;&#x1f44d;收藏&#x1f31f;&#x1f31f;关注&#x1f496;&#x1f496; 你的支持是对我最大的鼓励&#xff0c;我们一起努力吧!&#x1f603;&am…...

可视耳勺方便吗?可视耳勺热销第一名品牌!

在生活中&#xff0c;耳部清洁是我们常常会关注却又容易忽视细节的一项日常护理。传统挖耳勺有着不可视的局限性&#xff0c;只能凭感觉和经验反复刮蹭耳朵&#xff0c;很容易将耳垢越捅越深&#xff0c;而且还会刮伤耳道。因此&#xff0c;可视耳勺应运而生&#xff0c;它通过…...

micropython 3-wire spi 9bit 写入的问题

网上猛找把&#xff0c;没有&#xff0c;找不到&#xff0c;mpy不愧是没朋友的缩写&#xff0c;没有咋办&#xff0c;自己造&#xff01; 此库特别适用那些rgb屏的初始化&#xff0c;大多用3线spi&#xff0c;好家伙rgb用了十多个引脚现在想起来省引脚了是吧&#xff0c;就差这…...

导致JVM内存泄露的ThreadLocal详解

1. ThreadLocal介绍 1.1 什么是ThreadLocal Java官方文档中的描述&#xff1a;ThreadLocal类用来提供线程内部的局部变量。这种变量在多线程环境下访问&#xff08;通过get和set方法访问&#xff09;时能保证各个线程的变量相对独立于其他线程内的变量。ThreadLocal实例通常来…...

windows下关闭解除占用端口的进程

环境&#xff1a;windows 10 场景&#xff1a;启动某一应用程序时&#xff0c;提示其他应用已占用此端口&#xff0c;比如端口2425。 解决步骤&#xff1a; 1/3、打开windows的命令提示符&#xff0c;输入以下命令&#xff0c;查找占用此端口2425的PID号&#xff1a; # win…...

Baumer工业相机堡盟工业相机如何通过NEOAPI SDK获取相机当前数据吞吐量(Python)

Baumer工业相机堡盟工业相机如何通过NEOAPI SDK里函数来获取相机当前数据吞吐量&#xff08;Python&#xff09; Baumer工业相机Baumer工业相机的数据吞吐量的技术背景CameraExplorer如何查看相机吞吐量信息在NEOAPI SDK里通过函数获取相机接口吞吐量 Baumer工业相机通过NEOAPI…...

版权与开源协议:一场创新与自由的边界之争

在数字时代的浪潮中&#xff0c;版权与开源协议作为知识产权领域的两大支柱&#xff0c;既相互依存又暗自较劲&#xff0c;共同绘制着科技创新的宏伟蓝图。本文将带您深入这场创新与自由的边界之争&#xff0c;探讨版权与开源协议之间的微妙关系&#xff0c;以及它们如何共同推…...

学生用的蓝牙耳机推荐有哪些?实测四款实力出众机型!

在当今数字化学习环境中&#xff0c;学生对蓝牙耳机的需求日益增长&#xff0c;无论是在线课程的学习、图书馆的集中阅读还是日常通勤中的音频资料复习&#xff0c;一款性能优异、舒适度高且价格合理的蓝牙耳机对学生来说至关重要&#xff0c;面对市场上琳琅满目的产品选择&…...

MIT6.824 课程-GFS

GFS 原文&#xff1a;https://zhuanlan.zhihu.com/p/113161014 搬运用于参考学习 概述 存储&#xff08;Storage&#xff09;是一个非常关键的抽象&#xff0c;用途广泛。 GFS 论文还提到了很多关于容错、备份和一致性的问题。 GFS 本身是 Google 内部一个很成功的实用系统&…...

力扣第200题 岛屿数量

前言 记录一下刷题历程 力扣第200题 岛屿数量 岛屿数量 原题目&#xff1a; 给你一个由 ‘1’&#xff08;陆地&#xff09;和 ‘0’&#xff08;水&#xff09;组成的的二维网格&#xff0c;请你计算网格中岛屿的数量。 岛屿总是被水包围&#xff0c;并且每座岛屿只能由水平…...

协议头,wireshark,http

目录 协议头 ip头 udp头 mac层 网络工具 telnet wireshark Http 一、HTTP 协议介绍 二、HTTP 协议的工作过程 三、使用抓包工具抓取报文 四、获取到http请求报文&#xff1a; 五、http请求&#xff08;request&#xff09; &#xff08;一&#xff09;、认识URL 项…...

vscode ssh离线远程连接ubuntu调试

遇见问题&#xff1a; 1 ssh连接上无法启动服务器的虚拟环境&#xff1b; 2 ssh连接上启动服务器的虚拟环境后无法打断点&#xff1b; 对于问题需要参考下面连接安装python和debugy的插件拓展&#xff0c;并且配置json文件link。VSCode - 离线安装扩展python插件教程_vscode…...

Jenkins 通过 Version Number Plugin 自动生成和管理构建的版本号

步骤 1&#xff1a;安装 Version Number Plugin 登录 Jenkins 的管理界面。进入 “Manage Jenkins” -> “Manage Plugins”。在 “Available” 选项卡中搜索 “Version Number Plugin”。选中并安装插件&#xff0c;完成后可能需要重启 Jenkins。 步骤 2&#xff1a;配置…...

零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?

一、核心优势&#xff1a;专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发&#xff0c;是一款收费低廉但功能全面的Windows NAS工具&#xff0c;主打“无学习成本部署” 。与其他NAS软件相比&#xff0c;其优势在于&#xff1a; 无需硬件改造&#xff1a;将任意W…...

【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15

缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下&#xff1a; struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...

在rocky linux 9.5上在线安装 docker

前面是指南&#xff0c;后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...

Go 语言接口详解

Go 语言接口详解 核心概念 接口定义 在 Go 语言中&#xff0c;接口是一种抽象类型&#xff0c;它定义了一组方法的集合&#xff1a; // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的&#xff1a; // 矩形结构体…...

转转集团旗下首家二手多品类循环仓店“超级转转”开业

6月9日&#xff0c;国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解&#xff0c;“超级…...

基础测试工具使用经验

背景 vtune&#xff0c;perf, nsight system等基础测试工具&#xff0c;都是用过的&#xff0c;但是没有记录&#xff0c;都逐渐忘了。所以写这篇博客总结记录一下&#xff0c;只要以后发现新的用法&#xff0c;就记得来编辑补充一下 perf 比较基础的用法&#xff1a; 先改这…...

IT供电系统绝缘监测及故障定位解决方案

随着新能源的快速发展&#xff0c;光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域&#xff0c;IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选&#xff0c;但在长期运行中&#xff0c;例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...

JVM虚拟机:内存结构、垃圾回收、性能优化

1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...

基于 TAPD 进行项目管理

起因 自己写了个小工具&#xff0c;仓库用的Github。之前在用markdown进行需求管理&#xff0c;现在随着功能的增加&#xff0c;感觉有点难以管理了&#xff0c;所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD&#xff0c;需要提供一个企业名新建一个项目&#…...

【分享】推荐一些办公小工具

1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由&#xff1a;大部分的转换软件需要收费&#xff0c;要么功能不齐全&#xff0c;而开会员又用不了几次浪费钱&#xff0c;借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...