拓扑排序:0,2,5,1,3,4,6,7,8,9
etv:顶点的最早发生时间
ltv:顶点的最晚发生时间
节点的etv=ltv,就是图的关键路径;
1、计算etv[0-9] etv[0]的值为0;etv[2]的值为4;etv[5]的值为4+7=11;etv[1]的值为3;etv[3]的值为3+5=8与4+8=12之间选择,选择最大的为12;
etv[4]的值为3+6=9与12+3=15之间选择,选择最大的为15;
etv[6]的值为15+9=24;etv[7]的值为15+4=19与11+6=17之间选择,选择最大的19;etv[8]=19+5=24;etv[9]=24+3=27与24+2=26之间选择最大的;
if(temp->weight+etv[cur]>etv[temp->arcVertex])
{
etv[temp->arcVertex]=temp->weight+etv[cur];
}
2、计算ltv[0-9] ltv的计算采用拓扑排序结果的倒序,先初始化ltv[0-9]的值为etv[9]的值27,ltv[8]=ltv[9]-3=24; ltv[7]=ltv[8]-5=19; ltv[6]=ltv[9]-2=25;
ltv[4]有两条弧线(4-6)(4-7) ltv[7]-4=15 ltv[6]-9=16 选择最小的15
ltv[3]=15-3=12; ltv[1]也有两条,选址最小的ltv[3]-5=7; ltv[5]=ltv[7]-6=13;
ltv[2]也有两条,选择最小的ltv[3]-8=4; ltv[0]两条选择最小的ltv[2]-4=0;
etv[1]=3,ltv[1]=7 表明v1的事情从第七天开始做,也不会影响工程的进度。因为v0->v3要12天,v0->v1->v3只要保证12天完成,v1->v3是5天,所以v0->v1最大可以是7天。
程序
main.cpp
#include<iostream>
#include<stdio.h>
#include <stdlib.h>
#include<string>
#include"structfun.h"
using std::string;
using std::printf;
using std::scanf;
using std::endl;
using std::to_string;
void main()
{
//图的邻接矩阵
//AdjMarGraph amg;
//CreateAdjMaxGraph(&amg);//创建图的邻接矩阵
AdjMarDepthFirstSearch(&amg,0);//图的邻接矩阵深度优先遍历
AdjMarBreadthFirstSearch(&amg);//广度优先遍历(递归)
// //AMBFS(&amg);//(非递归)
AdjMarGraphLeastPath(&amg);//图-最小生成路径(采用邻接矩阵)prim算法
printf("\n");
AdjMarGraphLeastPathKruskal(&amg);//图-最小生成路径(采用邻接矩阵)kruskal算法
//ShortestPath(&amg,9,0);//自己写的
//
//pathmatirx *p;
//shortpathtable *D;
//p=(pathmatirx*)malloc(sizeof(pathmatirx));
//D=(shortpathtable*)malloc(sizeof(shortpathtable));
//shortestpathA(&amg,4,p,D);//教科书上的
//shortestpathFloyd(&amg,0,9);//Floyd最短路径弗洛伊德算法
//图的邻接表
AdjListGraph alg;
CreateAdjListGraph(&alg);
TopologicalSort(&alg);//直接调用定点删除函数的方法
//TopologicalSortA(&alg);//拓扑排序
CriticalPath(&alg);//图邻接表-关键路径查找
//printf("删除的定点是:%s",DeleteAdjListVertex(&alg,0).c_str());
//AdjListDepthFirstSearch(&alg,0);//图的深度遍历
//AdjListBreadthFirstSearch(&alg);//图的广度遍历
//图的十字链表
//OrthogonalListGraph olg;
//CreateOrthogonalListGraph(&olg);
//图的邻接多重表
//AdjacencyMultipleTableGraph amtg;
//CreateAdjacencyMultipleTableGraph(&amtg);
system("pause");
}
structfun.h
//数据结构函数头文件
#include <stdio.h>
#include <iostream>
#include<string>
using std::cout;
using std::cin;
using std::string;
#define MAXSIZE 100
#define OK 1
#define ERROR 0
#define INFINITY 6430//无穷大
typedef string ElemType;
typedef string VertexType;//图顶点数据类型
typedef int EdgeType;//图边的数据类型
//定义一个数组队列
typedef struct Queue
{
int date[MAXSIZE];
int num;
};
typedef struct stack{
ElemType data[MAXSIZE];
int top;
}stackList;
//初始化栈
int InitStack(stackList *L);
//入栈push
int push(stackList *L,ElemType e);
//出栈pop
ElemType pop(stackList *L);
//数据结构-图-----------------------------------------------------------------------------
//1、邻接矩阵(Adjacency Martix)
//设置一个顶点数组vertex[],设置一个边数组arc[][]
typedef struct
{
//图顶点数组
VertexType vertex[MAXSIZE];
//图边数组
EdgeType arc[MAXSIZE][MAXSIZE]; //边的值就是权重
int vertexnum,arcnum;
}AdjMarGraph;
//2、邻接表(Adjacency List)
//设置一个顶点数组vertex[],设置一个边的链表,顶点的next指向所对应的链表
typedef struct EdgeNode//边链表节点
{
EdgeType arcVertex;
int weight;//权值
EdgeNode *next;
}EdgeNode;
typedef struct//顶点数组
{
VertexType vertex;
EdgeNode *firstEdge;
int in;//入度
}VertexList[MAXSIZE];
typedef struct//图结构
{
VertexList vlist;
int numVertex,numArc;
}AdjListGraph;
//3、十字链表(Orthogonal->正交的 List)
//设置一个顶点数组vertex[],设置一个边的链表,顶点的next指向所对应的链表
//链表节点 tailvex headvex headlink,taillink; 改成invex,outvex,outlink,inlink更好理解
//入顶点,出顶点,出链接,入链接
typedef struct OrthogonalLinkNode
{
EdgeType invex,outvex;
OrthogonalLinkNode *outlink,*inlink;
int weight;//边的权重
}OrthogonalLinkNode;
//数组顶点节点 vertex 顶点 edgein 入边 edgeout 出边
typedef struct
{
VertexType vertex;
OrthogonalLinkNode *edgein,*edgeout;
}OrthogonalEdgeNode[MAXSIZE];
//十字链表结构
typedef struct
{
OrthogonalEdgeNode vertexlist;
int numvertex,numedge;
}OrthogonalListGraph;
//4、无向图 邻接多重表结构Adjacency Multiple Table(邻接多重表就是一个顶点的多条边用链表链接)
//ivex ilink jvex jlink (ivex jvex 边的两个顶点,ilink指向值与ivex相同的jvex的节点,jlink指向值与jvex相同的下一个节点。
typedef struct AMTEdgeNode//边链表节点
{
VertexType iver,jver;
bool visited;//是否被访问过
int weight;//权值
AMTEdgeNode *ilink,*jlink;
}AMTEdgeNode;
typedef struct//顶点数组
{
VertexType vertex;
AMTEdgeNode *firstEdge;
}AMTVertexList[MAXSIZE];
typedef struct//图结构
{
AMTVertexList vlist;
int numVertex,numArc;
}AdjacencyMultipleTableGraph;
//5、图的边集数组
typedef struct
{
int begin;//开始定点下标
int end;//结束顶点下标
int w;//权重
}EdgeArray[MAXSIZE];
//1、邻接矩阵
//无向图的邻接矩阵
int CreateAdjMaxGraph(AdjMarGraph *amg);
int AdjMarDepthFirstSearch(AdjMarGraph *amg,int f);//邻接矩阵深度遍历 int f为开始的结点
int AdjMarBreadthFirstSearch(AdjMarGraph *amg);
int AMBFS(AdjMarGraph *amg);
//图-最小生成路径(采用邻接矩阵)
int AdjMarGraphLeastPath(AdjMarGraph *amg);//prim算法
int AdjMarGraphLeastPathKruskal(AdjMarGraph *amg);//Kruskal算法
//图-两个顶点之间的最小路径(ShortestPath)
int ShortestPath(AdjMarGraph *amg,int begin,int end);
#define MAXVER 9
typedef int pathmatirx[MAXVER];
typedef int shortpathtable[MAXVER];
void shortestpathA(AdjMarGraph *amg,int v0,pathmatirx *p,shortpathtable *D);
void shortestpathFloyd(AdjMarGraph *amg,int begin,int end);
//2、邻接表
int CreateAdjListGraph(AdjListGraph *alg);
string DeleteAdjListVertex(AdjListGraph *alg,int vertexcur);//删除顶点
void TopologicalSort(AdjListGraph *alg);//拓扑排序直接删除定点
void TopologicalSortA(AdjListGraph *alg,stackList *stack1,int *etv[MAXSIZE],int *ltv[MAXSIZE]);//拓扑排序改变入度
void CriticalPath(AdjListGraph *alg);//图邻接表-关键路径查找
int AdjListDepthFirstSearch(AdjListGraph *alg,int f);//邻接表深度遍历
int AdjListBreadthFirstSearch(AdjListGraph *alg);//邻接表广度遍历
structfun.cpp
#include<iostream>
#include<stdio.h>
#include<string>
#include<sstream>
#include"structfun.h"
using std::cout;
using std::cin;
using std::string;
using std::endl;
using std::ostringstream;
using std::stoi;
using std::to_string;
//初始化栈
int InitStack(stackList *L)
{
for(int i=0;i<MAXSIZE;i++)
(*L).data[i]="";
(*L).top=-1;
return 1;
}
//入栈push
int push(stackList *L,ElemType e)
{
//
if((*L).top>MAXSIZE)
return 0;
(*L).top=(*L).top+1;
(*L).data[(*L).top]=e;
return 1;
}
//出栈pop
ElemType pop(stackList *L)
{
ElemType e;
if((*L).top==-1)
return 0;
e=(*L).data[(*L).top].c_str();
(*L).data[(*L).top]="";
(*L).top=(*L).top-1;
return e;
}
int CreateAdjListGraph(AdjListGraph *alg)
{
int i=0;
alg->numVertex=0;
alg->numArc=0;
FILE *fp=fopen("4.txt","rb");//文件使用方式由r,w,a,t,b,+各字符的含义是:r(read): 只读 w(write): 只写 a(append): 追加 t(text): 文本文件,可省略不写 b(binary): 二进制文件 +: 读和写
while(1)//创建顶点
{
printf("请输入邻接表的顶点结束用#:");
fscanf(fp,"%s",alg->vlist[i].vertex.c_str());
printf("%s\n",alg->vlist[i].vertex.c_str());
//cin>>alg->vlist[i].vertex;
if(*alg->vlist[i].vertex.c_str()=='#')
break;
alg->vlist[i].firstEdge=NULL;
alg->vlist[i].in=0;//初始化入度为0
i++;
alg->numVertex++;
}
while(1)//创建边链表
{
int v1=0,v2=0,w=0;
EdgeNode *edgenode;
printf("请输入邻接表的顶点对应的边(v1,v2,w)以(0,0,0)结束:");
fscanf(fp,"%d,%d,%d",&v1,&v2,&w);
printf("%d,%d,%d\n",v1,v2,w);
if(!v1&&!v2&&!w)//(0,0,0)结束
break;
alg->vlist[v2].in++;//目的边的入度加1
edgenode = new EdgeNode();//创建边节点
edgenode->arcVertex=v2;
edgenode->weight=w;
edgenode->next=NULL;
if(alg->vlist[v1].firstEdge==NULL)//插入边节点
alg->vlist[v1].firstEdge=edgenode;
else
{
EdgeNode *temp;
temp=alg->vlist[v1].firstEdge;
while(temp->next!=NULL)
temp=temp->next;
temp->next=edgenode;
}
//无向图要添加反方向的边,跟上面的代码一样,只是v1换成v2 v2换成v1
/*edgenode = new EdgeNode();//创建边节点
edgenode->arcVertex=v1;
edgenode->weight=w;
edgenode->next=NULL;
if(alg->vlist[v2].firstEdge==NULL)//插入边节点
alg->vlist[v2].firstEdge=edgenode;
else
{
EdgeNode *temp;
temp=alg->vlist[v2].firstEdge;
while(temp->next!=NULL)
temp=temp->next;
temp->next=edgenode;
}
alg->numArc++;*/
}
return OK;
}
//删除邻接表定点,传入要删除定点的下标,返回成功该定点;
string DeleteAdjListVertex(AdjListGraph *alg,int vertexcur)
{
string str;
str=alg->vlist[vertexcur].vertex.c_str();
//处理入度减1
EdgeNode *temp;
temp=alg->vlist[vertexcur].firstEdge;
while(temp)
{
alg->vlist[temp->arcVertex].in--;//减入度
alg->numArc--;//减边数
temp=temp->next;
}
if(vertexcur<0&&vertexcur>alg->numVertex)
return 0;
alg->vlist[vertexcur].firstEdge=NULL;
int vertexcur1=vertexcur;
while(vertexcur<alg->numVertex) //删除顶点
{
alg->vlist[vertexcur].vertex=alg->vlist[vertexcur+1].vertex.c_str();
alg->vlist[vertexcur].firstEdge=alg->vlist[vertexcur+1].firstEdge;
alg->vlist[vertexcur].in=alg->vlist[vertexcur+1].in;
vertexcur++;
}
alg->numVertex--;//定点数减1
//所有边链表下标减1
for(int i=0;i<alg->numVertex;i++)
{
temp=alg->vlist[i].firstEdge;
while(temp)
{
if(temp->arcVertex>=vertexcur1)
{temp->arcVertex--;}
temp=temp->next;
}
}
return str;
}
//拓扑排序
void TopologicalSort(AdjListGraph *alg)
{
//定义一个栈,存储入度为零的定点
stackList stack;
InitStack(&stack);
string vc;
int count=0,vnum=alg->numVertex;
while(1)//判断入度是否为零,为零删除定点
{
for(int i=0;i<alg->numVertex;i++)
{
if(alg->vlist[i].in==0)
{
push(&stack,alg->vlist[i].vertex.c_str());
printf("%s->",alg->vlist[i].vertex.c_str());//打印被删除的定点
DeleteAdjListVertex(alg,i);
count++;
i--;
}
}
vc=pop(&stack).c_str();
//printf("%s->",vc.c_str());
if(stack.top==-1)
{
if(count==vnum)
printf("AOV网不存在环");
else
printf("AOV网存在环");
break;
}
}
}
void TopologicalSortA(AdjListGraph *alg,stackList *stack1,int etv[],int ltv[])
{
//定义一个数组
stackList stack;
InitStack(&stack);
InitStack(stack1);
int count=0,i,max=0,min;
//查找入度为零的定点
for(i=0;i<alg->numVertex;i++)
{
if(alg->vlist[i].in==0)
{
push(&stack,to_string((long long)i));//入度为零的顶点下标入栈
}
}
//处理并打印
while(stack.top!=-1)
{
string vstr;
int cur;
EdgeNode *temp;
temp=(EdgeNode*)malloc(sizeof(EdgeNode));
vstr=pop(&stack);
cur=stoi(vstr.c_str());
printf("%s->",alg->vlist[cur].vertex.c_str());
push(stack1,to_string((long long)cur));//排序结果存入栈2,用于计算ltv,ltv采用倒序,符合栈的特点
//9,8,7,6,4,3,1,5,2,0
count++;
//遍历该定点连接的其他边,把他们的入度减一
temp=alg->vlist[cur].firstEdge;
//etv[cur]=max;
while(temp!=NULL)
{
if(temp->weight+etv[cur]>etv[temp->arcVertex])//选择较大的etv值存入etv数组。用于查找关键路径
{
etv[temp->arcVertex]=temp->weight+etv[cur];
}
//temp->weight>etv[cur]?etv[cur]=max+temp->weight:etv[cur];
if(!--alg->vlist[temp->arcVertex].in)
push(&stack,to_string((long long)temp->arcVertex));//入度为零的顶点下标入栈
temp=temp->next;
}
}
//计算数组ltv值,倒序从定点9开始
for(int i=0;i<alg->numVertex;i++)//初始化ltv为最后一个定点的etv
ltv[i]=etv[alg->numVertex-1];
while(stack1->top!=-1)
{
int cur=stoi(pop(stack1).c_str());//栈2中取出顶点下标
EdgeNode *temp;
temp=(EdgeNode*)malloc(sizeof(EdgeNode));
temp=alg->vlist[cur].firstEdge;
while(temp!=NULL)//循环遍历顶点所连接的边
{
if(ltv[temp->arcVertex]-temp->weight<ltv[cur])//选择较小的etv值存入ltv数组。用于查找关键路径
{
ltv[cur]=ltv[temp->arcVertex]-temp->weight;
}
temp=temp->next;
}
}
if(count==alg->numVertex)
printf("AOV网不存在环");
else
printf("AOV网存在环");
}
//图邻接表-关键路径查找
void CriticalPath(AdjListGraph *alg)
{
int etv[MAXSIZE],ltv[MAXSIZE];//定义顶点最早发生时间etv,最晚发生时间ltv
int ete=0,lte=0,max,min;//定义最早开工时间ete,最晚开工时间lte
for(int i=0;i<alg->numVertex;i++)//初始化
{
etv[i]=ltv[i]=0;
}
stackList stack1;
TopologicalSortA(alg,&stack1,etv,ltv);//拓扑排序,并计算出etv,ltv
//输出关键路径
printf("\n工程的关键路径为:");
for(int i=0;i<alg->numVertex;i++)
if(ltv[i]==etv[i])
printf("->%s",alg->vlist[i].vertex.c_str());
}