C# · 12月 20, 2021

数据结构–C语言–图的深度优先遍历,广度优先遍历,拓扑排序,用prime算法实现最小生成树,用迪杰斯特拉算法实现关键路径和关键活动的求解,最短路径

实验七  图的深度优先遍历(选做,验证性实验,4学时)

实验目的

熟悉图的数组表示法和邻接表存储结构,掌握构造有向图、无向图的算法 ,在掌握以上知识的基础上,熟悉图的深度优先遍历算法,并实现。

实验内容

(1)图的数组表示法定义及基本操作的实现。

(2)图的邻接表表示法定义及基本操作的实现。

(3)写函数实现图的深度优先遍历(分别在两种结构上)

(4)在邻接表上实现拓扑排序、关键路径的求法,在邻接矩阵上实现最短路经、最小生成树的求法。

//邻接矩阵表示法存储无向图 深度优先搜索 最短路径 最小生成树

#include

#define MAX_VERTEX_NUM 20

#define INT_MAX unsigned(-1)>>1

typedef int VRType;

typedef int Status;

typedef char VertexType;

typedef char* InfoType;

typedef int PathMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];

typedef int ShortPathTable[MAX_VERTEX_NUM];

int visited[MAX_VERTEX_NUM];//全局变量 访问标志数组

typedef enum{DG,DN,UDG,UDN}GraphKind;

typedef struct{

VertexType adjvex;

int lowcost;

}Closedge[MAX_VERTEX_NUM];//存放所有已经插入的节点到当前顶点的权值最小的那条边 终点就是i位置上的顶点 adj存放边的起点 lowcost存放最小的权值

typedef struct{

VRType adj;//顶点关系

InfoType info;//该弧相关信息的指针 –权值

}ArcCell,AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];

typedef struct{

VertexType vexs[MAX_VERTEX_NUM];//顶点向量

AdjMatrix arcs;//邻接矩阵

int vexnum,arcnum;

GraphKind kind;//图的种类标志

}MGraph;

int LocateVex(MGraph G,VertexType a){

for(int i=0;i<G.vexnum;i++)

if(G.vexs[i]==a) return i;

}

Status CreatUDN(MGraph &G){

int IncInfo;

G.kind = GraphKind(UDN);

printf(“请输入顶点数 边数 边上是否有相关信息(是1否0)n”);

scanf(“%d%d%d”,&G.vexnum,&G.arcnum,&IncInfo);//顶点个数 边数

//先构造顶点向量 给顶点数组赋值

fflush(stdin);

printf(“请输入顶点集:n”);

for(int i=0;i<G.vexnum;i++) scanf(" %c",&G.vexs[i]);

//初始化邻接矩阵

for(int i=0;i<G.vexnum;i++)

for(int j=0;j<G.vexnum;j++)

G.arcs[i][j].adj=INT_MAX;//初始化全是断路

printf(“请输入弧头弧尾和权值n”);

for(int i=0;i<G.arcnum;i++){

char head,tail;//弧头弧尾的两个字符

int weight,headlo,taillo;//边的权值 弧头弧尾在顶点向量中的位置

fflush(stdin);

scanf(” %c %c %d”,&tail,&head,&weight);

headlo = LocateVex(G,head);

taillo = LocateVex(G,tail);

G.arcs[taillo][headlo].adj = weight;

if(IncInfo) scanf(“%s”,G.arcs[headlo][taillo].info);

// G.arcs[taillo][headlo] = G.arcs[headlo][taillo];//无向图的边的关系在矩阵里是对称的

}

}

Status Visit(VertexType c){

printf(“%c”,c);

}

int FirstAdjVex(MGraph G,int v){

for(int j=0;j<G.vexnum;j++)

if(G.arcs[v][j].adj) return j;

}

int NextAdjVex(MGraph G,int v,int w){

for(int j=w+1;j<G.vexnum;j++)

if(G.arcs[v][j].adj) return j;

return -1;

}

Status DFS(MGraph G,int i){

visited[i] = 1;

Visit(G.vexs[i]);

for(int w=FirstAdjVex(G,i);w>=0;w=NextAdjVex(G,i,w))

if(!visited[w])

DFS(G,w);

return 0;

}

Status DFSTraverse(MGraph G,Status (*Visit)(VertexType c)){//深度优先遍历算法

for(int i=0;i<G.vexnum;i++)

visited[i] = 0;

for(int i=0;i<G.vexnum;i++)

if(!visited[i])

DFS(G,i);

return 0;

}

void MiniSpanTree_PRIME(MGraph G,VertexType u){//从u顶点出发开始构造最小生成树

int k = LocateVex(G,u),minlocate;//新插入节点在顶点向量里的下标

Closedge closedge;

for(int i=0;i<G.vexnum;i++){//辅助数组初始化

if(i!=k){

closedge[i].adjvex = u;//边的起点

closedge[i].lowcost = G.arcs[k][i].adj;//边的权值

}

}

closedge[k].lowcost = 0;

minlocate = 0;

for(int i=1;i<G.vexnum;i++){//选择其余的i-1个顶点

for(int m=0;m<G.vexnum;m++){

if(!closedge[m].lowcost) continue;

if(!closedge[minlocate].lowcost) minlocate = m;

if(closedge[minlocate].lowcost>closedge[m].lowcost) minlocate = m;

}

k = minlocate;//选出权值最小的边的终点的下标

printf(“n%c %c”,closedge[k].adjvex,G.vexs[k]);

closedge[k].lowcost = 0;//第k顶点已经插入在树里了 所以不会存在以第k顶点为终点的待插入的边了

for(int j=0;j<G.vexnum;j++){

if(G.arcs[k][j].adj<closedge[j].lowcost){

closedge[j].lowcost = G.arcs[k][j].adj;

closedge[j].adjvex = G.vexs[k];

}

}

}

}

void ShortestPath_DIJ(MGraph G,VertexType e,PathMatrix &P,ShortPathTable &D){

int final[MAX_VERTEX_NUM];

int v0 = LocateVex(G,e);

int newin,min;

//初始化D[] P[][] final[]

for(int i=0;i<G.vexnum;i++){

D[i] = G.arcs[v0][i].adj;

final[i] = 0;//均设置为没有找到最短路径

for(int j=0;j<G.vexnum;j++) P[i][j] = 0;//设置空路径

if(D[i]<INT_MAX){

P[i][v0] = 1;//权值不是无穷大 说明存在通路

P[i][i] = 1;

}

}

D[v0] = 0;//从源点到源点的最小路径为0

final[v0] = 1;//初始化v0 v0属于S集

//开始主循环 每次求得v0到某个顶点v的最短路径 并加v到S集

for(int i=1;i<G.vexnum;i++){

min = INT_MAX;

for(int j=0;j<G.vexnum;j++){

if(!final[j]){//min存放的是当前与v0最近的的节点的路径 newin是最近的顶点的下标 也就是即将插入S的顶点

if(D[j]<min){

min = D[j];

newin = j;

}

}

}

final[newin] = 1;//将第j个顶点插入S中 说明该顶点已经找到了最短路径

for(int j=0;j<G.vexnum;j++){

if(!final[j]&&(D[newin]+G.arcs[newin][j].adj>0)&&(D[newin]+G.arcs[newin][j].adj<D[j])){//修改D[w]和P[w]

D[j] = D[newin]+G.arcs[newin][j].adj;

for(int m=0;m<G.vexnum;m++) P[j][m] = P[newin][m];

P[j][newin] = 1;

P[j][j]=1;

}

}

}

printf(“endvextpastvextpathlengthn”);

for(int i=0;i<G.vexnum;i++){

printf(” %c t”,G.vexs[i]);

if(D[i]<INT_MAX){

for(int j=0;j<G.vexnum;j++) if(P[i][j]) printf("%c",G.vexs[j]);

printf(” t%dn”,D[i]);

}else{

printf(“无n”);

}

}

}

main(){

MGraph G;

CreatUDN(G);

printf(“nn深度优先遍历:”);

DFSTraverse(G,Visit);

printf(“nn最小生成树的构成:”);

MiniSpanTree_PRIME(G,’a’);

PathMatrix P;

ShortPathTable D;

printf(“nn从源点a到其余各点的最短路径:n”);

ShortestPath_DIJ(G,’a’,P,D);

return 0;

}

//邻接表表示法存储有向图 广度优先搜索拓 扑排序 关键路径

#include

#include

#define MAXSIZE 20

#define MAX_VERTEX_NUM 20

typedef int Status;

typedef int QElemType;//队列里放的是元素在一位数组里的下标

typedef char VertexType;

typedef int SElemType;

typedef enum{DG,UDN}GraphKind;

typedef struct{

QElemType *base;//队列的基地址

int front;//队头下标 相当于队头指针 指向队列头元素

int rear;//队尾下标 指向队列尾元素的下一个位置

}SqQue;

typedef struct ArcNode{//表结点

int adjvex;//该弧指向的顶点的位置

int weight;//权重

int info;

struct ArcNode* next;

}ArcNode;

typedef struct{//头结点

VertexType data;//顶点信息

ArcNode *firstarc;//指向第一个邻接点

}VNode,AdjList[MAX_VERTEX_NUM];

typedef struct{

AdjList vertices;

int vexnum,arcnum;

int kind;

}ALGraph;

typedef struct{

SElemType *base;

SElemType *top;

int stacksize;

}SqStack;

int visited[MAX_VERTEX_NUM];

int ve[MAX_VERTEX_NUM];

int vl[MAX_VERTEX_NUM];

int indegree[MAX_VERTEX_NUM];

SqStack S,T;//S是零入度顶点栈 T是拓扑序列顶点栈

int LocateVex(ALGraph G,VertexType v){

for(int i=0;i<G.vexnum;i++)

if(G.vertices[i].data==v) return i;

}

int FirstAdjVex(ALGraph G,int v){

if(!G.vertices[v].firstarc) return -1;//不存在邻接点

return G.vertices[v].firstarc->adjvex;

}

int NextAdjVex(ALGraph G,int w){

ArcNode *p;

for(p=G.vertices[v].firstarc;p;p=p->next)

if(p->adjvex==w&&p->next) return p->next->adjvex;

return -1;

}

Status Visit(VertexType c){

printf(“%c”,c);

}

//初始化一个空栈

Status InitStack(SqStack &S){

S.base = (SElemType *)malloc(MAX_VERTEX_NUM*sizeof(SElemType));

if(!S.base) exit(-1);

S.top = S.base;

S.stacksize = MAX_VERTEX_NUM;

return 1;

}

//判空

Status StackEmpty(SqStack S){

return S.base==S.top;

}

//入栈

Status Push(SqStack &S,SElemType e){

*S.top++ = e;

return 1;

}

//出栈

Status Pop(SqStack &S,SElemType &i){

if(S.base==S.top){

printf(“空栈!n”);

return 0;

}

i = *–S.top;

return 1;

}

Status CreatDN(ALGraph &G){

int IncInfo;

printf(“请输入顶点数 边数 边上是否有相关信息(是1否0)n”);

scanf(“%d%d%d”,&IncInfo);//顶点个数 边数

//先构造顶点向量

fflush(stdin);

printf(“请输入顶点集:n”);

for(int i=0;i<G.vexnum;i++){

scanf(” %c”,&G.vertices[i].data);

G.vertices[i].firstarc = NULL;//初始化指针为空

}

printf(“请输入弧头 弧尾 “);

if(IncInfo) printf(“边上的关系:n”);

for(int i=0;i<G.arcnum;i++){

char head,taillo;//边的权值 弧头弧尾在顶点向量中的位置

ArcNode *pnew;

fflush(stdin);

pnew = (ArcNode*)malloc(sizeof(ArcNode));

if(!pnew) exit(0);

scanf(” %c %c”,&tail);

if(IncInfo)

scanf(“%d”,&(pnew->info));

headlo = LocateVex(G,head);

pnew->adjvex = LocateVex(G,tail);

pnew->next = G.vertices[headlo].firstarc;

G.vertices[headlo].firstarc = pnew;//头插法插入表结点

}

}

//构造一个空的循环队列

Status InitQueue(SqQue &q){

q.base = (QElemType *)malloc(MAXSIZE*sizeof(QElemType));

if(!q.base) exit(0);

q.front = q.rear = 0;//空队列 两指针均指向下表为0的位置

return 1;

}

//出队列

Status DeQueue(SqQue &q,int &e){

if(q.rear==q.front) return 0;

e = q.base[q.front];

q.front = (q.front+1)%MAXSIZE;

return 1;

}

//入队列

Status EnQueue(SqQue &q,int e){

if((q.rear+1)%MAXSIZE==q.front){

printf(“队列已满!n”);

return 0;

}

q.base[q.rear] = e;

q.rear = (q.rear+1)%MAXSIZE;

return 1;

}

Status BFSTraverse(ALGraph G,Status (*Visit)(VertexType c)){//深度优先遍历算法

for(int i=0;i<G.vexnum;i++) visited[i] = 0;

SqQue q;

int u;//保存出队列元素

InitQueue(q);

for(int i=0;i<G.vexnum;i++){

if(!visited[i]){

visited[i] = 1;

Visit(G.vertices[i].data);

EnQueue(q,i);//将下标入队列 而不是元素值

while(q.rear!=q.front){

DeQueue(q,u);

for(int j=FirstAdjVex(G,u);j>=0;j=NextAdjVex(G,u,j)){

if(!visited[j]){

visited[j] = 1;

Visit(G.vertices[j].data);

EnQueue(q,j);

}

}

}

}

}

}

Status FindInDegree(ALGraph G){

ArcNode *p;

for(int i=0;i<G.vexnum;i++) indegree[i] = 0;

for(int i=0;i<G.vexnum;i++)

for(p=G.vertices[i].firstarc;p;p=p->next)

indegree[p->adjvex]++;

}

Status TopologicalSort(ALGraph G){

SqStack S;

int i,j,count;

ArcNode* p;

FindInDegree(G);

InitStack(S);

for(i=0;i<G.vexnum;i++)

if(!indegree[i]) Push(S,i);//将入度为0的顶点的下标入栈

count = 0;//对输出顶点进行计数

while(!StackEmpty(S)){

Pop(S,i);//将入度为0的顶点位置出栈

printf(“%c”,G.vertices[i].data);//输入出栈顶点的内容

count++;//计数器+1

for(p=G.vertices[i].firstarc;p;p=p->next){

j = p->adjvex;

if(!(–indegree[j])) Push(S,j);

}

}

if(count<G.vexnum) printf("该有向图中有环存在n");

}

Status TopologicalOrder(ALGraph G){

int i,count=0;

ArcNode *p;

FindInDegree(G);

InitStack(S);InitStack(T);

for(i=0;i<G.vexnum;i++)

if(!indegree[i])

Push(S,i);//将入度为0的顶点的下标入栈

for(int i=0;i<G.vexnum;i++) ve[i] = 0;//ve[i]指的是事件i的最早开始时间

while(!StackEmpty(S)){

Pop(S,i);

Push(T,i);

count++;//和拓扑排序里的大致一样

for(p=G.vertices[i].firstarc;p;p=p->next){

j = p->adjvex;

if((ve[i]+p->info)>ve[j]) ve[j] = ve[i] + p->info;//计算入度为0的顶点的邻接点的最早开始时间

indegree[j]–;

if(!indegree[j]){

Push(S,j);

}

}

}

if(count<G.vexnum) return 0;

return 1;

}

Status CriticalPath(ALGraph G){

int j,k,dut,ee,el;//ee是活动的最早开始时间 el是活动的最晚开始时间

ArcNode *p;

if(!TopologicalOrder(G)) return 0;//如果该带权有向图存在环的话就有活动以自身为开始条件 出现错误

for(int i=0;i<G.vexnum;i++) vl[i] = ve[G.vexnum-1];

while(!StackEmpty(T)){//求事件的最晚发生时间

for(Pop(T,j),p=G.vertices[j].firstarc;p;p=p->next){

k = p->adjvex;

dut = p->info;//关键活动的时间

if(vl[k]-dut<vl[j]) vl[j] = vl[k]-dut;

}

}

printf(“starteventtendeventtlasttimetstarttimen”);

for(int j=0;j<G.vexnum;j++){

for(p=G.vertices[j].firstarc;p;p=p->next){

k = p->adjvex;

dut = p->info;

ee = ve[j];

el = vl[k]-dut;

if(ee==el) printf(” %ct %ct %dt %dn”,G.vertices[j].data,G.vertices[k].data,ee);

}

}

}

main(){

ALGraph G;

CreatDN(G);

printf(“nn广度优先搜索:”);

BFSTraverse(G,Visit);

printf(“nn拓扑排序结果:”);

TopologicalSort(G);

printf(“nn关键活动:n”);

CriticalPath(G);

return 0;

}