数据结构之有关图的算法(图的矩阵表示法)
本篇意在总结有关图的一些基本算法
包括有图的最小生成树(Prim,Kruscal),最短路径(Dijkstra,Floyd),拓扑排序等算法
本篇图的数据结构为矩阵表示法
// project1.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include<string.h> #include<assert.h> #include<stdio.h> #include<stdlib.h> #define MAX 100 #define INFINITY (unsigned int)-1 //邻接矩阵表示法 struct MGraph{ char vexs[MAX];//顶点 unsigned int edges[MAX][MAX];//边 int vexnum,edgenum; int kind; //图的类型,0无向图,1有向图,2带权无向图,3带权有向图 MGraph(int vn=0,int en=0,int kind=0):vexnum(vn),edgenum(en),kind(kind){ } }; //通过输入创建图矩阵 void createMGraphFromStdin(MGraph *g){ assert(g); printf("input vexnum, edgenum, and graph kind:\n"); assert(scanf("%d,%d,%d",&g->vexnum,&g->edgenum,&g->kind)==3); fflush(stdin); printf("input every vertex info:\n"); for(int i=0;i<g->vexnum;i++){//初始化顶点信息 assert(scanf("%c",&g->vexs[i])==1); fflush(stdin); } char svex,tvex; int weight; switch(g->kind){ case 0://无向图 memset(g->edges,0,sizeof(int)*MAX*MAX); printf("input edges with format i,j\n"); for(int k=0;k<g->edgenum;k++){ assert(scanf("%c,%c",&svex,&tvex)==2); fflush(stdin); int i,j; for(i=0;g->vexs[i]!=svex;i++);//找到编号 for(j=0;g->vexs[j]!=tvex;j++); g->edges[i][j]=g->edges[j][i]=1; } break; case 1://有向图 memset(g->edges,0,sizeof(int)*MAX*MAX); printf("input edges with format i,j\n"); for(int k=0;k<g->edgenum;k++){ assert(scanf("%c,%c",&svex,&tvex)==2); fflush(stdin); int i,j; for(i=0;g->vexs[i]!=svex;i++);//找到编号 for(j=0;g->vexs[j]!=tvex;j++); g->edges[i][j]=1; } break; case 2://带权无向图 memset(g->edges,0xFF,sizeof(int)*MAX*MAX); printf("input edges with format i,j,weight\n"); for(int k=0;k<g->edgenum;k++){ g->edges[k][k]=0; assert(scanf("%c,%c,%d",&svex,&tvex,&weight)==3); fflush(stdin); int i,j; for(i=0;g->vexs[i]!=svex;i++);//找到编号 for(j=0;g->vexs[j]!=tvex;j++); g->edges[i][j]=g->edges[j][i]=weight; } break; case 3://带权有向图 memset(g->edges,0xFF,sizeof(int)*MAX*MAX); printf("input edges with format i,j,weight\n"); for(int k=0;k<g->edgenum;k++){ g->edges[k][k]=0; assert(scanf("%c,%c,%d",&svex,&tvex,&weight)==3); fflush(stdin); int i,j; for(i=0;g->vexs[i]!=svex;i++);//找到编号 for(j=0;g->vexs[j]!=tvex;j++); g->edges[i][j]=weight; } break; } } //通过文件创建图矩阵 void createMGraphFromFile(MGraph *g,char *file){ assert(g && file); FILE* fp=fopen(file,"r"); assert(fp); char str[256]; if(fgets(str,255,fp)!=NULL){ sscanf(str,"%d,%d,%d",&g->vexnum,&g->edgenum,&g->kind); } for(int i=0;i<g->vexnum;i++){//初始化顶点信息 if(fgets(str,255,fp)!=NULL){ sscanf(str,"%c",&g->vexs[i]); } } char svex,tvex; int weight; switch(g->kind){ case 0://无向图 memset(g->edges,0,sizeof(int)*MAX*MAX); for(int k=0;k<g->edgenum;k++){ if(fgets(str,255,fp)!=NULL){ sscanf(str,"%c,%c",&svex,&tvex); } int i,j; for(i=0;g->vexs[i]!=svex;i++);//找到编号 for(j=0;g->vexs[j]!=tvex;j++); g->edges[i][j]=g->edges[j][i]=1; } break; case 1://有向图 memset(g->edges,0,sizeof(int)*MAX*MAX); for(int k=0;k<g->edgenum;k++){ if(fgets(str,255,fp)!=NULL){ sscanf(str,"%c,%c",&svex,&tvex); } int i,j; for(i=0;g->vexs[i]!=svex;i++);//找到编号 for(j=0;g->vexs[j]!=tvex;j++); g->edges[i][j]=1; } break; case 2://带权无向图 memset(g->edges,0xFF,sizeof(int)*MAX*MAX); for(int k=0;k<g->edgenum;k++){ g->edges[k][k]=0; if(fgets(str,255,fp)!=NULL){ sscanf(str,"%c,%c,%d",&svex,&tvex,&weight); } int i,j; for(i=0;g->vexs[i]!=svex;i++);//找到编号 for(j=0;g->vexs[j]!=tvex;j++); g->edges[i][j]=g->edges[j][i]=weight; } break; case 3://带权有向图 memset(g->edges,0xFF,sizeof(int)*MAX*MAX); for(int k=0;k<g->edgenum;k++){ g->edges[k][k]=0; if(fgets(str,255,fp)!=NULL){ sscanf(str,"%c,%c,%d",&svex,&tvex,&weight); } int i,j; for(i=0;g->vexs[i]!=svex;i++);//找到编号 for(j=0;g->vexs[j]!=tvex;j++); g->edges[i][j]=weight; } break; } } int findVex(MGraph* g,char vex){ for(int i=0;i<g->vexnum;i++) if(g->vexs[i]==vex) return i; return -1; } //最小生成树之Prim,Kruscal算法 //松弛操作 void relaxation(MGraph *g,unsigned int dist[],char vex){ int j=findVex(g,vex); assert(j!=-1); for(int i=0;i<g->vexnum;i++){ if(j==i) dist[i]=0; else if(g->edges[j][i] <dist[i])//<j,i> dist[i]=g->edges[j][i]; } } void relaxation_Dijkstra(MGraph* g,unsigned int dist[],char svex,char tvex){ int i=findVex(g,svex); int j=findVex(g,tvex); assert(j!=-1 && i!=-1); for(int k=0;k<g->vexnum;k++){ if(g->edges[j][k]!=INFINITY && (dist[j]+g->edges[j][k])<dist[k]){ dist[k]=dist[j]+g->edges[j][k]; } } } int findMin(unsigned int dist[],int length){ unsigned int min=INFINITY; int pos=-1; for(int i=0;i<length;i++){ if(dist[i] !=0 && dist[i]<min){ min=dist[i]; pos=i; } } return pos; } //sp[vexnum]标记源点到该点是否已是最短路径 int findMin_Dijkstra(unsigned int dist[],int length,bool sp[]){ unsigned int min=INFINITY; int pos=-1; for(int i=0;i<length;i++){ if(sp[i]==false && dist[i] !=0 && dist[i]<min){ min=dist[i]; pos=i; } } return pos; } void MST_Prim(MGraph* g,char beg_vex){ unsigned int dist[MAX];//辅助数组 char prim_vex[MAX];//记录依次加入的点 int vexs=0;//记录已加入点的数目 prim_vex[vexs++]=beg_vex; relaxation(g,dist,beg_vex); while(vexs<g->vexnum){ int i=findMin(dist,g->vexnum); assert(i!=-1); prim_vex[vexs++]=g->vexs[i]; relaxation(g,dist,g->vexs[i]); } prim_vex[vexs]='\0'; printf("Prim MST sequence:%s\n",prim_vex); } struct EdgeType{ int s,t; unsigned int cost; }; int cmp(const void* a,const void *b){ return (*(EdgeType *)a).cost - (*(EdgeType *)b).cost; } //寻找vex所在树的根结点 int findFather(int father[],int vex){ int t=vex; while(father[t]>=0) t=father[t]; return t; } void MST_Kruscal(MGraph* g,char beg_vex){ int k=0; EdgeType *edges = new EdgeType[g->edgenum]; for(int i=0;i<g->vexnum;i++) for(int j=i+1;j<g->vexnum;j++){ if(g->edges[i][j]<INFINITY){ edges[k].s=i;edges[k].t=j; edges[k++].cost=g->edges[i][j]; } if((g->kind==1 || g->kind==3) && g->edges[j][i]<INFINITY){//如果有向 edges[k].s=j;edges[k].t=i; edges[k++].cost=g->edges[j][i]; } } qsort(edges,g->edgenum,sizeof(EdgeType),cmp); int father[MAX];//初始所有点属于不同的连通分量 for(int i=0;i<g->edgenum;i++)//初始化 father[i]=-1; int j=0,i=0; while(j<g->edgenum && i<g->vexnum){ int svex=findFather(father,edges[j].s); int tvex=findFather(father,edges[j].t); if(svex != tvex){ father[tvex]=svex; i++; printf("<%c,%c> ",g->vexs[svex],g->vexs[tvex]); } j++; } delete[] edges; printf("\n"); } void ShortestPath_Dijkstra(MGraph* g,char source_vex){ unsigned int sp_dist[MAX];//最短路径数组 bool sp[MAX]={false};//标记是否已是最短 int j=findVex(g,source_vex); assert(j!=-1); memset(sp_dist,0xFF,sizeof(int)*MAX); sp_dist[j]=0; sp[j]=true; relaxation_Dijkstra(g,sp_dist,source_vex,source_vex); char tvex; int rounds=1; while(rounds<g->vexnum){ int j=findMin_Dijkstra(sp_dist,g->vexnum,sp); assert(j!=-1); sp[j]=true; relaxation_Dijkstra(g,sp_dist,source_vex,g->vexs[j]); rounds++; } for(int i=0;i<g->vexnum;i++) printf("Shortest Path from %c to %c is %d\n",source_vex,g->vexs[i],sp_dist[i]); } //若P[v][w][u]为TRUE,则u 是从v 到w 当前求得的最短路径上的顶点 bool P[MAX][MAX][MAX]; unsigned int D[MAX][MAX]; void ShortestPath_Flyod(MGraph *g){ /* 使用一个n*n的方阵D,D[s][t]表示<s,t>的最短路径 * 但是为了D[s][t],需要更新n次D矩阵 * D(k)[s][t]表示经过k次更新后,当前<s,t>的最短路径,可能最终不是 * D(-1)[s][t]=edges[s][t] * D(k)[s][t]=min{ D(k-1)[s][t],D(k-1)[s][k]+D(k-1)[k][t] } * D(k)的计算是尝试把顶点k加到每对顶点<s,t>之间 */ memset(D,0xFF,sizeof(unsigned int)*MAX*MAX); for(int i=0;i<g->vexnum;++i){//初始化D矩阵 for(int j=0;j<g->vexnum;++j){ D[i][j]=g->edges[i][j]; //D(-1) for(int u=0;u<g->vexnum;++u) P[i][j][u]=false; if (D[i][j]<INFINITY){ /*从v 到w 有直接路径*/ P[i][j][i]=true; P[i][j][j]=true; } } } for(int k=0; k<g->vexnum; ++k){//计算D(k),总共n次,这个循环一定要在最外层 for(int s=0; s<g->vexnum; ++s){//D(k)[s] for(int t=0;t<g->vexnum;++t){//D(k)[s][t] if(D[s][k]<INFINITY && D[k][t]<INFINITY && (D[s][k]+D[k][t])<D[s][t]){ //如果顶点k属于<s,t>最短路径上 D[s][t]=D[s][k]+D[k][t]; //更新P[s][t],当前P[s][t]最短路径上有哪些顶点 for(int i=0;i<g->vexnum;++i) P[s][t][i]=P[s][k][i] || P[k][t][i]; } } } } //输入每对顶点的最短路径上的顶点 for(int s=0;s<g->vexnum;s++) for(int t=0;t<g->vexnum;++t){ printf("The Shortest Path Vertexes of <%c,%c> are:\n",g->vexs[s],g->vexs[t]); for(int i=0;i<g->vexnum;i++) if(P[s][t][i]) printf("%c ",g->vexs[i]); printf("\n"); } } //拓扑排序 void Topo_Sort(MGraph* g){ char topo[MAX];//保存拓扑排序的顶点序列 int vexs=0; bool visited[MAX];//记录顶点是否已排序 int indegree[MAX];//记录顶点的入度 for(int i=0;i<g->vexnum;i++){//初始化入度数组 indegree[i]=0; visited[i]=false; for(int j=0;j<g->vexnum;j++){ if(i!=j && g->edges[j][i]<INFINITY) indegree[i]++; } } while(vexs<(g->vexnum-1)){//Topo_Sort int i; for(i=0;i<g->vexnum;i++){//寻找入度为0的顶点 if(indegree[i]==0 && visited[i]==false){ topo[vexs++]=g->vexs[i]; visited[i]=true; //修改入度 for(int j=0;j<g->vexnum;j++){ if(j!=i && g->edges[i][j]<INFINITY) indegree[j]--; } break; } } if(i>=g->vexnum){ printf("Cannot Topo Sort!\n"); return; } } topo[vexs]='\0'; printf("Topo Sort: %s\n",topo); } int _tmain(int argc, _TCHAR* argv[]) { char beg_vex='f'; MGraph *g=new MGraph(); createMGraphFromStdin(g); if(g->kind==2 || g->kind==3) MST_Prim(g,beg_vex); MGraph *g2=new MGraph(); char *file="./in.txt"; createMGraphFromFile(g2,file); if(g2->kind==2 || g2->kind==3){ MST_Prim(g2,beg_vex); MST_Kruscal(g2,beg_vex); ShortestPath_Dijkstra(g2,beg_vex); ShortestPath_Flyod(g2); Topo_Sort(g2); } delete g2; return 0; }