第6章 图的基本算法

来源:互联网 发布:志鸿优化设计历史答案 编辑:程序博客网 时间:2024/05/19 15:40

一、最小生成树和最短路径的区别

         最小生成树是指生成树各边的权值总和最小的树,保证整体树的权值是最小,并不保证任意两点间的权值最小。

         最短路径是保证从起始点到指定其余点的权值和最小,即求从起点到其余各点路径上权值和最小的路径。

二、3种主要算法实现     

#if ! defined(MGRAPH_H)#define MGRAPH_H#include<limits.h>#include<stdio.h>#include<stdlib.h>#define MaxVertexNum 50typedef char VertexType;//顶点类型typedef int Adjmatrix;//边的权值typedef struct{VertexType vexs[MaxVertexNum];Adjmatrix arcs[MaxVertexNum][MaxVertexNum];//有边为指定数值,没有为INT_MAX}MGraph;/********Prim算法********基本思路:首先,选中指定1个顶点作为源点。然后,选择从该顶底到其余顶点中权值最小的点,并加入到源点中。再次,从源点中选择到其余顶点中权值最小的点。重复以上过程。*/typedef int VRType;//对应顶点在矩阵中的索引typedef enum{FREE,USED} State;//辅助顶点的状态typedef struct{    VertexType ver;//顶点类型    VRType lowcost;//边的权重    State used;//取值为FREE,USED}minedge;//从某个顶点到其余边的最小权重typedef minedge Edges[MaxVertexNum];//获取顶点在G中的下标int VtxNum(MGraph G,VertexType u,int n){    //G中有顶点和边的数组,u顶点,n顶点总数    int i;    for(i=0;i<n;i++){        if(G.vexs[i]==u){            return i;        }    }    return -1;}int minEdge(Edges edges,int n){//min是系统函数    //获取最小值    int i;    int min=-1;//min是lowcost的最小值索引    int temp=0;    int templowcost;    for(i=0;i<n;i++){        if(edges[i].used==FREE){            if(temp==0){                //获取第一个最小值                temp=1;                templowcost=edges[i].lowcost;                min=i;            }else if(temp==1){                //获取最小的                if(edges[i].lowcost<templowcost){                    templowcost=edges[i].lowcost;                    min=i;                }//end if            }//end if        }    }//end for    return min;}//获取void Prim(MGraph G,VertexType u,int n){    //G矩阵表,u起始顶点,n顶点总数    int k,v,j,pre;    Edges edges;//定义辅助边数组        k=VtxNum(G,u,n);//获取顶点u在辅助数组中的位置    //辅助数组初始化    for(v=0;v<n;v++){        if(v!=k){                        edges[v].ver=u;//各辅助数组记录各种点到u点的权重            edges[v].lowcost=G.arcs[k][v];            edges[v].used=FREE;        }else{            //本节点            edges[v].lowcost=0;            edges[v].used=USED;            }    }                for(j=1;j<n;j++){        k=minEdge(edges,n);//k是u到其他点最小的权重边对应的点                //******显示******//        printf("v%c-v%c\n",edges[k].ver,G.vexs[k]);        edges[k].used=USED;//第k个顶点并入U        for(v=0;v<n;v++){            if(edges[v].used==FREE && G.arcs[k][v]<edges[v].lowcost){                //各点到k节点,所在边的小权重                edges[v].ver=G.vexs[k];                edges[v].lowcost=G.arcs[k][v];            }        }//end for    }//end for}/*******Kruskal算法基本思路:选择最小的边,判断两个顶点是否已经在生成树中,若没有添加到生成树中;若已经在最小生成书中则放弃这条边。1.对边排序,按照。2.选择最小的边。3.判断边的两点是否已经在最小生成树中。4.重复2和3。*******/typedef struct{    int v1;//第一个顶点    int v2;//第二个顶点    int lowcost;//两点之间边的权重}Edge;//Kruskal辅助边算法typedef Edge KEdges[MaxVertexNum*MaxVertexNum];//辅助数组void KSort(KEdges KE,int n){    //对辅助数组排序    Edge e;    int i,j,m=n*n;    //直接插入排序算法    for(i=1;i<m;i++){        if(KE[i].lowcost<KE[i-1].lowcost){            e=KE[i];            j=i;            do{                KE[j]=KE[j-1];//往后移位                j--;            }while(j-1>=0 && e.lowcost<KE[j-1].lowcost );//这个条件和if的差不多            KE[j]=e;        }    }}void Kruskal(MGraph G,int n){    KEdges kedges;//辅助数组    int vectextag[MaxVertexNum]={0};//顶点出现次数标记    int i,j,k=0,m;    //1.初始化辅助数组    for(i=0;i<n;i++){        for(j=0;j<n;j++){            kedges[k].v1=i;            kedges[k].v2=j;            kedges[k].lowcost=G.arcs[i][j];            k++;        }    }//end for    //2.对边数组排序    KSort(kedges,n);    //3.生成树    k=1;//用来标记相应顶点出现次数    m=n*n;//边的条数    for(i=0;i<m;i++){//不能简单适用i+=2于无向图,虽然因无向图边(i,j)和(j,i)的权值是相同的,但也有可能其他边的权值是相同的。        if(INT_MAX == kedges[i].lowcost) continue;//如果边是无穷大就不需要执行加入树的操作        if(0 == vectextag[kedges[i].v1] && 0== vectextag[kedges[i].v2]){            //新边两个新顶点            vectextag[kedges[i].v1]=k;            vectextag[kedges[i].v2]=k;            k++;        }else if(0== vectextag[kedges[i].v1] && 0!=vectextag[kedges[i].v2]){            //v2顶点已经在最小生成树中            vectextag[kedges[i].v1]=vectextag[kedges[i].v2];//v2顶点所在树标志赋予v1顶点。        }else if(0!= vectextag[kedges[i].v1] && 0==vectextag[kedges[i].v2]){            vectextag[kedges[i].v2]=vectextag[kedges[i].v1];//v1顶点所在树标志赋予v2顶点。        }else{            //没有新节点,当2个顶点标志相同时说明它们已经在同一颗树            if(vectextag[kedges[i].v1] == vectextag[kedges[i].v2]) continue;            //不同的小生成树,修改为同一棵生成树            for(j=0;j<n;j++){                if(vectextag[j] == vectextag[kedges[i].v1]){                    //把等于v1的修改为顶点v2的点                    vectextag[j]=vectextag[kedges[i].v2];                }            }//end for        }//end if        //***访问边***//        printf("v%c-v%c\n",G.vexs[kedges[i].v1],G.vexs[kedges[i].v2]);    }//end for 遍历边    }/*******Dijkstra求最短路径目标:求指定起点到其他各顶点的最短路径和权重。基本思路:1.求指定点到其他顶点的权重。2.把各个点分别加入到其中时,比较权重之和。3.若小于原先的顶点,则替换新值********/void Dijkstra(MGraph G,int v0,int n){    int Distance[MaxVertexNum];//指定点到各顶点的权重。    int Path[MaxVertexNum]={-1};//Path的数值是由近到远。    int S[MaxVertexNum];//1表示顶点Source已经在源中了,0表示没有。    int v,i,min,j;    //1.初始化权重、路径和是否在源中    for(v=0;v<n;v++){        S[v]=0;//是否在源中        Distance[v]=G.arcs[v0][v];//权重        if(Distance[v]<INT_MAX){            //有边            Path[v]=v0;//父节点为v0        }    }    Distance[v0]=0;//源点到自身的距离为0    S[v0]=1;//把v0点加入到源点中        //2.生成最短路径树    for(i=1;i<n;i++){//i=1是应为v0已经在源点的集合中,剩余n-1条边                min=INT_MAX;//最小的权重        for(j=0;j<n;j++){            if(!S[j] && Distance[j]<min){                v=j;//v0到其他顶点的最小权重                min=Distance[j];            }        }        S[v]=1;//把该顶点的最小权重边添加到源中        //更新权重表和路径        for(j=0;j<n;j++){            if( !S[j] &&                 !(Distance[v]==INT_MAX || G.arcs[v][j]==INT_MAX) && //左边不能有INT_MAX,如果有会上溢                (Distance[v]+G.arcs[v][j]<Distance[j])){//走新节点v比原来的小                                    Distance[j]=Distance[v]+G.arcs[v][j];//更改为更小的路线                    Path[j]=v;//j的父节点变成v            }        }//end for    }//end for    //3.访问路径    for(i=0;i<n;i++){        printf("v%c-(%d)-v%c\n",G.vexs[v0],Distance[i],G.vexs[i]);//显示v0到        }}int main(){    void PrimTest();    void DijkstraTest();        PrimTest();    DijkstraTest();        return 0;}void PrimTest(){    MGraph G1={        {'1','2','3','4','5','6'},//顶点        {//矩阵            {INT_MAX,6,1,5,INT_MAX,INT_MAX},//1到其他各个顶点            {6,INT_MAX,5,INT_MAX,3,INT_MAX},//2到其他各个顶点            {1,5,INT_MAX,5,6,4},//3到其他各个顶点            {5,INT_MAX,5,INT_MAX,INT_MAX,2},            {INT_MAX,3,6,INT_MAX,INT_MAX,6},            {INT_MAX,INT_MAX,4,2,6,INT_MAX}        }    };    Prim(G1,'1',6);    printf("\n\n");    Kruskal(G1,6);    printf("\n\n");    }void DijkstraTest(){    MGraph G2={        {'1','2','3','4','5','6'},//顶点        {//矩阵            //  1       2       3       4       5       6            {INT_MAX,    3    ,INT_MAX,    20    ,INT_MAX,INT_MAX},//1到其他各个顶点            {INT_MAX,INT_MAX,    2    ,INT_MAX,    4   ,INT_MAX},//2到其他各个顶点            {INT_MAX,INT_MAX,INT_MAX,    7    ,    1    ,    5    },//3到其他各个顶点            {INT_MAX,INT_MAX,INT_MAX,INT_MAX,INT_MAX,INT_MAX},            {INT_MAX,INT_MAX,INT_MAX,INT_MAX,INT_MAX,INT_MAX},            {INT_MAX,INT_MAX,    3    ,INT_MAX,INT_MAX,INT_MAX}        }    };    Dijkstra(G2,0,6);}#endif









0 0