第6章 图的基本算法

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

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

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

二、3种主要算法实现     

#if ! defined(MGRAPH_H)
#define MGRAPH_H

#include<limits.h>
#include<stdio.h>
#include<stdlib.h>


#define MaxVertexNum 50

typedef 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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值