zoukankan      html  css  js  c++  java
  • 算法学习记录图——最小生成树之prim算法

    一个连通图的生成树是一个极小的连通子图,它包含图中全部的顶点(n个顶点),但只有n-1条边。

    最小生成树:构造连通网的最小代价(最小权值)生成树。

    prim算法在严蔚敏树上有解释,但是都是数学语言,很深奥。

    最小生成树MST性质:假设N=(V,{E})是一个连通网,U是顶点集V的一个非空子集。若(u,v)是一条具有最小权值(代价)的边,

    其中u∈U,v∈V-U,则必存在一颗包含边(u,v)的最小生成树。

    prim算法过程为:

    假设N=(V,{E})是连通图,TE是N上最小生成树中边的集合。算法从U={u0}(u0∈V),TE={}开始,

    重复执行下述操作:

    在所有u∈U,v∈V-U的边(u,v)∈E中找一条代价最小的边(u0,v0)并入集合TE,同时v0 并入U,直至U=V为止。

    此时TE中必有n-1条边,则T=(V,{TE})为N的最小生成树。

    我以图为例,看看算法过程。

     

    上面基本就把prim算法思想给表达出来。

    代码部分:

    这里我使用的是邻接矩阵来表示图,其中边的值就是权值。

    #define MAXVEX 100
    #define IFY 65535
    
    typedef char VertexType;
    typedef int  EdgeType;
    //静态图-邻接矩阵
    typedef struct {
        VertexType vexs[MAXVEX];
        EdgeType Mat[MAXVEX][MAXVEX];
        int numVexs,numEdges;
    }MGraph;
    VertexType g_init_vexs[MAXVEX] = {'A','B','C','D','E','F','G','H','I'};
    
    EdgeType g_init_edges[MAXVEX][MAXVEX] = {
        {0,11,IFY,IFY,IFY,9,IFY,IFY,6},    //'A'
        {11,0,10,IFY,IFY,IFY,IFY,IFY,8},  //'B'
        {IFY,10,0,17,IFY,IFY,IFY,IFY,IFY},//'C'
        {IFY,IFY,17,0,9,IFY,IFY,IFY,IFY},//'D'
        {IFY,IFY,IFY,9,0,7,5,8,IFY},    //'E'
        {9,IFY,IFY,IFY,7,0,3,IFY,IFY},    //'F'
        {IFY,IFY,IFY,IFY,5,3,0,IFY,IFY},    //'G'
        {IFY,IFY,IFY,IFY,8,IFY,IFY,0,IFY},    //'H'
        {6,8,IFY,IFY,IFY,IFY,IFY,IFY,0},    //'I'
    };


    prim算法代码:

    void prim(MGraph G,int num)
    {
        int sum=0;
        int min,i,j,k;
        int adjvex[MAXVEX];
        int lowcost[MAXVEX];
    
        lowcost[num] = 0;
        adjvex[num] = 0;
    
        for (i = 0; i < G.numVexs;i++ )
        {
            if (num == i)
            {
                continue;
            }
            lowcost[i]=G.Mat[num][i];    //存放起始顶点到各个顶点的权值。
            adjvex[i] = num;
        }
    
        for (i=0;i<G.numVexs;i++)
        {
            //1.找权最短路径
            //2.把权最短路径的顶点纳入已找到的顶点集合中,重新查看新集合中最短路径
            if(num == i)
            {
                continue;
            }
            min = IFY;
            j=0;k=0;
            while (j<G.numVexs)
            {
                if (lowcost[j] != 0 && lowcost[j] < min)
                {
                    min = lowcost[j];
                    k = j;
                    
                }
                j++;
            }
            printf(" (%d,%d) --> ",adjvex[k],k);
            sum += G.Mat[adjvex[k]][k];
            lowcost[k]=0;
            for (j=0;j<G.numVexs;j++)
            {
                if (j == num)
                {
                    continue;
                }
                if (lowcost[j] != 0 && G.Mat[k][j] < lowcost[j])
                {
                    lowcost[j] = G.Mat[k][j];
                    adjvex[j]=k;
                }
            }
        }
        printf("\ntotal:sum=%d",sum);
    }

    我写的是一个可以指定入口的(即从哪个点)开始进行。测试每个入口,得到的路径应该是一样,且值也应该一样大。

    其中两个辅助数组:

    lowcost[]:用来存放 非U集合的点与U集合点的权值的最小值。其【x】里面的数字x,表示U中到V中顶点Vx的最小权值。(每次都会更新比较,保证其最小。)

          而归入到U集合的点,对应的lowcost中的元素是为0;之后就不再做比较。 

    adjvex[]:在每次归入新顶点后,都要对U与非U集合中权值比较,保持lowcost中的值为最小。此时改变的lowcost中的某个元素(即新纳入的顶点到非U集合的权值更小)

          此时,将改变的lowcost中序号x,将新纳入的顶点Vt与原先U集合中与之相连的点的序号存入adjvex【x】。这样 adjvex【x】中,x就是那些个要更新的

          lowcost【x】,adjvex【x】存放就是原先点。

    这也方便查找新加入的边(adjvex【k】,k)。

       

    基本上可以看出,adjvex【】作用:

      实质上lowcost[x] 是 边(x,adjvex[x])的权值。明白这一点,程序就非常好理解了。

    完整程序:

    // grp-mat-bfs-self.cpp : 定义控制台应用程序的入口点。
    //
    
    #include "stdafx.h"
    #include <stdlib.h>
    
    #define MAXVEX 100
    #define IFY 65535
    
    typedef char VertexType;
    typedef int  EdgeType;
    
    
    bool g_visited[MAXVEX];
    
    VertexType g_init_vexs[MAXVEX] = {'A','B','C','D','E','F','G','H','I'};
    
    EdgeType g_init_edges[MAXVEX][MAXVEX] = {
        {0,11,IFY,IFY,IFY,9,IFY,IFY,6},    //'A'
        {11,0,10,IFY,IFY,IFY,IFY,IFY,8},  //'B'
        {IFY,10,0,17,IFY,IFY,IFY,IFY,IFY},//'C'
        {IFY,IFY,17,0,9,IFY,IFY,IFY,IFY},//'D'
        {IFY,IFY,IFY,9,0,7,5,8,IFY},    //'E'
        {9,IFY,IFY,IFY,7,0,3,IFY,IFY},    //'F'
        {IFY,IFY,IFY,IFY,5,3,0,IFY,IFY},    //'G'
        {IFY,IFY,IFY,IFY,8,IFY,IFY,0,IFY},    //'H'
        {6,8,IFY,IFY,IFY,IFY,IFY,IFY,0},    //'I'
    };
    
    EdgeType g_init_edges_bak[MAXVEX][MAXVEX] = {
        {0,1,IFY,IFY,IFY,1,IFY,IFY,1},    //'A'
        {1,0,1,IFY,IFY,IFY,IFY,IFY,1},  //'B'
        {IFY,1,0,1,IFY,IFY,IFY,IFY,IFY},//'C'
        {IFY,IFY,1,0,1,IFY,IFY,IFY,IFY},//'D'
        {IFY,IFY,IFY,1,0,1,1,1,IFY},    //'E'
        {1,IFY,IFY,IFY,1,0,1,IFY,IFY},    //'F'
        {IFY,IFY,IFY,IFY,1,1,0,IFY,IFY},    //'G'
        {IFY,IFY,IFY,IFY,1,IFY,IFY,0,IFY},    //'H'
        {1,1,IFY,IFY,IFY,IFY,IFY,IFY,0},    //'I'
    };
    //==========================================================================
    //静态图-邻接矩阵
    typedef struct {
        VertexType vexs[MAXVEX];
        EdgeType Mat[MAXVEX][MAXVEX];
        int numVexs,numEdges;
    }MGraph;
    
    //====================================================================
    //打印矩阵
    void prt_maxtix(EdgeType *p,int vexs)
    {
        int i,j;
        for (i=0;i<vexs;i++)
        {
            printf("\t");
            for (j=0;j<vexs;j++)
            {
                if( (*(p + MAXVEX*i + j)) == IFY)
                {
                    printf("  $ ");
                }
                else
                {
                    printf(" %2d ", *(p + MAXVEX*i + j));
                }
            }
            printf("\n");
        }
    }
    
    //check the number of vextex
    int getVexNum(VertexType *vexs)
    {
        VertexType *pos = vexs;
        int cnt=0;
        while(*pos <= 'Z' && *pos >= 'A')
        {
            cnt++;
            pos++;
        }
        return cnt;
    }
    
    bool checkMat(EdgeType *p,VertexType numvex)
    {
        int i,j;
        for (i=0;i<numvex;i++)
        {
            for(j=i+1;j<numvex;j++)
            {
                //printf("[%d][%d] = %d\t",i,j,*(p + MAXVEX*i + j));
                //printf("[%d][%d] = %d\n",j,i,*(p + MAXVEX*j + i));
                if (*(p + MAXVEX*i + j) != *(p + MAXVEX*j +i) )
                {
                    printf("ERROR:Mat[%d][%d] or Mat[%d][%d] not equal!\n",i,j,j,i);
                    return false;
                }
            }
        }
        return true;
    }
    
    void init_Grp(MGraph *g,VertexType *v,EdgeType *p)
    {
        int i,j;
        // init vex num
        (*g).numVexs = getVexNum(v);
    
        //init vexter 
        for (i=0;i<(*g).numVexs;i++)
        {
            (*g).vexs[i]=*v;
            v++;
        }
    
        //init Mat 
        for (i=0;i<(*g).numVexs;i++)
        {
            for (j=0;j<(*g).numVexs;j++)
            {
                (*g).Mat[i][j] = *(p + MAXVEX*i + j);
            }
        }
        if(checkMat(&((*g).Mat[0][0]),(*g).numVexs) == false)
        {
            printf("init error!\n");
            exit(0);
        }
    }
    
    void prim(MGraph G,int num)
    {
        int sum=0;
        int min,i,j,k;
        int adjvex[MAXVEX];
        int lowcost[MAXVEX];
    
        lowcost[num] = 0;
        adjvex[num] = 0;
    
        for (i = 0; i < G.numVexs;i++ )
        {
            if (num == i)
            {
                continue;
            }
            lowcost[i]=G.Mat[num][i];    //存放起始顶点到各个顶点的权值。
            adjvex[i] = num;
        }
    
        for (i=0;i<G.numVexs;i++)
        {
            //1.找权最短路径
            //2.把权最短路径的顶点纳入已找到的顶点集合中,重新查看新集合中最短路径
            if(num == i)
            {
                continue;
            }
            min = IFY;
            j=0;k=0;
            while (j<G.numVexs)
            {
                if (lowcost[j] != 0 && lowcost[j] < min)
                {
                    min = lowcost[j];
                    k = j;
                    
                }
                j++;
            }
            printf(" (%d,%d) --> ",adjvex[k],k);
            sum += G.Mat[adjvex[k]][k];
            lowcost[k]=0;
            for (j=0;j<G.numVexs;j++)
            {
                
                if (lowcost[j] != 0 && G.Mat[k][j] < lowcost[j])
                {
                    lowcost[j] = G.Mat[k][j];
                    adjvex[j]=k;
                }
            }
            
        }
        printf("total:sum=%d\n",sum);
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        MGraph grp;
        //init
        init_Grp(&grp,g_init_vexs,&g_init_edges[0][0]);
        //print Matix
        prt_maxtix(&grp.Mat[0][0],grp.numVexs);
        
        //prim(grp,4);
        int i;
        for (i=0;i<grp.numVexs;i++)
        {
            prim(grp,i);
        }
        //prim(grp,3);
    
    
        getchar();
        return 0;
    }


    测试结果:

    最小生成树一样,而且总权值也一样。

  • 相关阅读:
    P1908 逆序对
    P3834 【模板】可持久化线段树 1(主席树)
    BZOJ 4300: 绝世好题
    Codevs 2185【模板】最长公共上升子序列
    P1439 【模板】最长公共子序列
    P3865 【模板】ST表
    【转】良心的可持久化线段树教程
    Codevs 1299 切水果
    P3388 【模板】割点(割顶)&& 桥
    P3805 【模板】manacher算法
  • 原文地址:https://www.cnblogs.com/jsgnadsj/p/3421288.html
Copyright © 2011-2022 走看看