数据结构之有关图的算法(图的矩阵表示法)

本篇意在总结有关图的一些基本算法

包括有图的最小生成树(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;
}

 

    原文作者:IT_cnblogs
    原文地址: https://www.cnblogs.com/abc123456789/p/3446167.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞