16 - 12 - 05 普里姆(Prim)算法-最小生成树-奥义

原创 2016年12月05日 21:47:25
/*
一定要先修正误区:两个数组绝对不是简单的一维数组,他们并不单纯,
一个数组元素可以说包含了几层含义。
先强调一遍:
lowcost[maxvex]数组的意义:
  lowcost[j]表示,在已经被纳入生成树的顶点中,他们到顶点 j 的最小权重的值。
  如:lowcost[3] = 8 表示顶点3到“已被纳入所有顶点”的最小权值是8.
adjvex[maxvex]数组的意义:
   adjvex[j] = x 表示, j 是未被纳入生成树的节点,他到已被纳入树的所有节点的最小权值是与 x相连而产生的。 
   如:adjvex[3] = 5 表示顶点3(未纳入)与顶点5(已纳入)之间存在最小权值。
   */

//算法思路:
//① 从图中任意找一个点,加入到最小生成树中;
//②在剩下的顶点中,找权重最小的边,将该顶点纳入最小生成树;
//③重复步骤②;
//```
/* 邻接矩阵表示的图结构*/
#include <stdio.h>
#include <stdlib.h>
//#include <curses.h>  这是啥?

typedef char VertexType;        //顶点类型应由用户定义
typedef int EdgeType;           //边上的权值类型应由用户定义

#define MAXVEX  100             //最大顶点数,应由用户定义
#define INFINITY 65535       //用65535来代表无穷大
#define DEBUG

/****邻接矩阵结构****/
typedef struct {
    VertexType vexs[MAXVEX];    //顶点表
    EdgeType   arc[MAXVEX][MAXVEX]; //邻接矩阵,可看作边
    int numVertexes, numEdges;      //图中当前的顶点数和边数
} Graph;

/****定位****/
int locates(Graph *g, char ch) {
    int i = 0;
    for(i = 0; i < g->numVertexes; i++) {
        if(g->vexs[i] == ch) {
            break;
        }
    }
    if(i >= g->numVertexes) {
        return -1;
    }
    return i;
}

/****建立一个无向网图的邻接矩阵表示****/ 
void CreateGraph(Graph *g) {
    int i, j, k, w;
    printf("输入顶点数,和边数(中间加个逗号~):\n");
    scanf("%d,%d", &(g->numVertexes), &(g->numEdges));

#ifdef DEBUG
    printf("%d %d\n", g->numVertexes, g->numEdges);
#endif

    for(i = 0; i < g->numVertexes; i++) {
        printf("请输入顶点%d:\n", i);
        g->vexs[i] = getchar();
        while(g->vexs[i] == '\n') {
            g->vexs[i] = getchar();
        }
    }

#ifdef DEBUG
    for(i = 0; i < g->numVertexes; i++) {
        printf("%c ", g->vexs[i]);
    }
    printf("\n");
#endif


    for(i = 0; i < g->numEdges; i++) {
        for(j = 0; j < g->numEdges; j++) {
            g->arc[i][j] = INFINITY;   //邻接矩阵初始化
        }
    }
    for(k = 0; k < g->numEdges; k++) {
        char p, q;
        printf("输入边(vi,vj)上的下标i,下标j和权值:\n");

        p = getchar();
        while(p == '\n') {
            p = getchar();
        }
        q = getchar();
        while(q == '\n') {
            q = getchar();
        }
        scanf("%d", &w);

        int m = -1;
        int n = -1;
        m = locates(g, p);
        n = locates(g, q);
        if(n == -1 || m == -1) {
            fprintf(stderr, "there is no this vertex.\n");
            return;
        }
        //getchar();     //????啥意思 
        g->arc[m][n] = w;
        g->arc[n][m] = g->arc[m][n];  //因为是无向图,矩阵对称
    }
}

/****打印图****/ 
/*这句话要明白:如:adjvex[3] = 5    lowcost[3] = 8 即表示序号为5的顶点到序号为3的顶点的边的权值为8*/
void printGraph(Graph g) {
    int i, j;
    printf("构建的邻接矩阵如下所示.\n");
    for(i = 0; i < g.numVertexes; i++) {
        for(j = 0; j < g.numVertexes; j++) {
            printf("%5d  ", g.arc[i][j]);
        }
        printf("\n");
    }
}

/****prim算法来一个最小生成树****/ 
/*
在实现该算法的过程中,主要用到两个数组lowcost[maxvex],adjvex[maxvex],只要理解这两个数组的意义,就能理解这个算法的实现。
lowcost[maxvex]数组的意义:
  lowcost[j]表示,在已经被纳入生成树的顶点中,他们到顶点 j 的最小权重的值。
  如:lowcost[3] = 8 表示顶点3到“已被纳入所有顶点”的最小权值是8.
adjvex[maxvex]数组的意义:
   adjvex[j] = x 表示, j 是未被纳入生成树的节点,他到已被纳入树的所有节点的最小权值是与 x相连而产生的。 
   如:adjvex[3] = 5 表示顶点3(未纳入)与顶点5(已纳入)之间存在最小权值。

   ex  :  adjvex[3] = 5  lowcost[3] = 8  即表示序号为5的顶点到序号为3的顶点的边的权值为8
*/
void MiniSpanTree_Prime(Graph g) 
{
    int min, i, j, k;
    int adjvex[MAXVEX];         //保存相邻接的(adjacent)顶点下标
    int lowcost[MAXVEX];        //保存相关顶点间边的权值,low cost(权值)
    lowcost[0] = 0;             //Vo加入生成树
    adjvex[0] = 0;        
    for(i = 1; i < g.numVertexes; i++)   /*FOR 1*/  
    {              
        /*循环除下标为0的顶点外的全部顶点,注意是全部,看看那张邻接矩阵图。*/
        lowcost[i] = g.arc[0][i];   /*将与Vo和其邻接点连成的边的权值存入数组*/
                                /*adjvex[0] = 0;lowcost[3]即表示Vo与下标为3的顶点的连线的权值*/                         
        adjvex[i] = 0;    /*初始化都为v0的下标  */
    }    /*adjvex[i] = 0,初始化所有未被纳入生成树的顶点到已被纳入生成树的顶点都是最小权值*/ 

    for(i = 1; i < g.numVertexes; i++)   /*FOR 2*/ 
    {
        min = INFINITY;    //初始化最小权值为无穷大
        j = 1;
        k = 0;
        while(j < g.numVertexes)
        {   //循环全部顶点
            if(lowcost[j] != 0 && lowcost[j] < min) { /*每次迭代,min都是INFINITY。别担心*/ 
                min = lowcost[j];   /*则让当前权值成为最小值(覆盖INFINITY)*/
                k = j;   /*若碰到比当前权值还小的,将这个最小值的下标存入k(后面lowcost[k] = 0要用到)*/
            }    /*当第二次循环时,就找到了 F 点,lowcost[5] = 6,第三趟循环时,lowcost里非零值只有7*/ 
            j++;   
        }   /*在第一次循环时,直到遍历完所有与Vo相关的顶点,找到与Vo邻接的权值最小的相邻顶点*/                                

        printf("(%d,%d)", adjvex[k], k); /*打印当前顶点边中权值最小边,第一次打印的是(0,3),第二遍打印(3,5)*/ 
        lowcost[k] = 0;     /*将“当前顶点与Vo”的权值设置为0,表示此顶点已OK(T:为什么要设置为零呢?)。PS:Vo仅代表当前头顶点*/ 
                    /*第二遍循环k = 5,lowcost[5] = 0,节点5(F)纳入生成树,lowcost[ 0 7 0 .. ..  ..]*/
                    /*第三趟循环 7比8小,7权额入树,即节点为 1的顶点B,lowcost[0 0 0 8  ..   ..  ]*/ 
        for(j = 1; j < g.numVertexes; j++)  /*FOR3:循环所有顶点,这里又定义了j,与上文的 j 无关系*/ 
        {  
            if(lowcost[j] != 0 && g.arc[k][j] < lowcost[j])  /*因为在上一步 k = j,所以现在以“刚才与头顶点之间权值最小的邻点”
                                                         为顶点来进行本轮搜索,从lowcost[j] != 0 这个条件可以看出刚才 lowcost[k] = 0的用意是别让他倒退搜索回去 */
                                                         /*第二趟循环k=5,g.arc[k][j] < lowcost[j]始终不满足,循环结束转向 FOR2。*/
                                                                    /*第三趟lowcost里是0,7和65535,FE是最小边,进lowcost*/ 
            {                       /*对g.arc[k][j] < lowcost[j]的理解:上文:lowcost[i] = g.arc[0][i],即:g.arc[k][j] < g.arc[0][j] “见附图”*/ 

                lowcost[j] = g.arc[k][j];    /*第一趟:lowcost[5] = arc[3][5]==6 ; */ 
                adjvex[j] = k;    /*第一趟里,j=5(F顶点);k=3(D顶点),*/ 
            }                   /*上文提到过,adjvex[5] = 3;表示顶点5(未纳入)与顶点3(已纳入)之间存在最小权值。*/ 
        }    /*结束FOR 3,转向 FOR 2循环*/ //
    }   /*第一趟结束时,lowcost[ 0 7 6..  .. ..  ..]*/ 
        /*第二趟结束,lowcost[ 0 0 7 8(EF) .. .. ..]*/ 

    printf("\n");
}

int main(int argc, char **argv) {
    Graph g;

    //邻接矩阵创建图
    CreateGraph(&g);
    //打印网图
    printGraph(g);
    //求最小生成树
    MiniSpanTree_Prime(g);

    return 0;
}

//```

有一步是这样描述的

这里写图片描述

这里写图片描述

另注:能否处理负权值?
不能,这与贪心选择性质有关,每次都找一个距源点最近的点(dmin),然后将该距离定为这个点到源点的最短
路径;但如果存在负权边,那就有可能先通过并不是距源点最近的一个次优点(dmin’),再通过这个负权边L(L<0),使得路径之和更小

版权声明:本文为博主原创文章,未经博主允许不得转载

相关文章推荐

C++ 最小生成树之Prim(普里姆)算法

最小生成树之Prime(普里姆)算法 最小生成树:是在一个给定的无向图G(V,E)中求一棵树T,使得这棵树拥有图G中的所有顶点,且所有边都是来自图G中的边,并且满足整棵树的边权之和最小。 如上...

java 普里姆(Prim)算法求图的最小生成树

1. 基本思想: 设G=(V,E)是连通网,T=(U,D)是最小生成树,V,U是顶点集合,E,D是边的集合 ①若从顶点u开始构造最小生成树,则从集合V中取出顶点u放入集合U中,标记顶点v的visi...
  • yxmmao
  • yxmmao
  • 2016年06月04日 22:05
  • 232

最小生成树之Prim(普里姆)算法

Prim算法是解决最小生成树的经典算法之一。本博客以个人的理解简略地对Prim算法进行介绍以及解剖。...

最小生成树之Prim(普里姆)算法

最小生成树之Prim(普里姆)算法

数据结构之---C语言实现最小生成树之prim(普里姆)算法

数据结构之---C语言实现最小生成树之prim(普里姆)算法

POJ 1251 Jungle Roads (最小生成树 Prim普里姆算法)

#include #define INF (1

最小生成树prim(普里姆)算法

#include #include #define Max 20 int visited[20]; typedef struct { char vex[Max]; int vexs[Max]...

图的最小生成树之普里姆Prim算法

源代码如下: #include using namespace std; #define MAX_VERTEX_NUM 20 #define infinity 9 typedef int QEle...

最小生成树(MST)----普里姆(Prim)算法与克鲁斯卡尔(Kruskal)算法

1、概念:给定一个带权的无向连通图,如何选取一棵生成树,使树上所有边上权的总和为最小,这叫最小生成树. 2、应用:例如:要在n个城市之间铺设光缆,主要目标是要使这 n 个城市的任意两个之间都可以通信...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:16 - 12 - 05 普里姆(Prim)算法-最小生成树-奥义
举报原因:
原因补充:

(最多只允许输入30个字)