Prim算法(一)之 C语言详解

出自:http://www.cnblogs.com/skywang12345/p/3711506.html

本章介绍普里姆算法。和以往一样,本文会先对普里姆算法的理论论知识进行介绍,然后给出C语言的实现。后续再分别给出C++和Java版本的实现。

目录 
1
普里姆算法介绍 
2普里姆算法图解 
3普里姆算法的代码说明 
4普里姆算法的源码

转载请注明出处:http://www.cnblogs.com/skywang12345/

更多内容:数据结构与算法系列 目录

普里姆算法介绍

普里姆(Prim)算法,和克鲁斯卡尔算法一样,是用来求加权连通图的最小生成树的算法。

基本思想 
对于图G而言,V是所有顶点的集合;现在,设置两个新的集合U和T,其中U用于存放G的最小生成树中的顶点,T存放G的最小生成树中的边。 从所有uЄU,vЄ(V-U) (V-U表示出去U的所有顶点)的边中选取权值最小的边(u, v),将顶点v加入集合U中,将边(u, v)加入集合T中,如此不断重复,直到U=V为止,最小生成树构造完毕,这时集合T中包含了最小生成树中的所有边。

普里姆算法图解

以上图G4为例,来对普里姆进行演示(从第一个顶点A开始通过普里姆算法生成最小生成树)。

初始状态:V是所有顶点的集合,即V={A,B,C,D,E,F,G};U和T都是空! 
第1步:将顶点A加入到U中。 
    此时,U={A}。 
第2步:将顶点B加入到U中。 
    上一步操作之后,U={A}, V-U={B,C,D,E,F,G};因此,边(A,B)的权值最小。将顶点B添加到U中;此时,U={A,B}。 
第3步:将顶点F加入到U中。 
    上一步操作之后,U={A,B}, V-U={C,D,E,F,G};因此,边(B,F)的权值最小。将顶点F添加到U中;此时,U={A,B,F}。 
第4步:将顶点E加入到U中。 
    上一步操作之后,U={A,B,F}, V-U={C,D,E,G};因此,边(F,E)的权值最小。将顶点E添加到U中;此时,U={A,B,F,E}。 
第5步:将顶点D加入到U中。 
    上一步操作之后,U={A,B,F,E}, V-U={C,D,G};因此,边(E,D)的权值最小。将顶点D添加到U中;此时,U={A,B,F,E,D}。 
第6步:将顶点C加入到U中。 
    上一步操作之后,U={A,B,F,E,D}, V-U={C,G};因此,边(D,C)的权值最小。将顶点C添加到U中;此时,U={A,B,F,E,D,C}。 
第7步:将顶点G加入到U中。 
    上一步操作之后,U={A,B,F,E,D,C}, V-U={G};因此,边(F,G)的权值最小。将顶点G添加到U中;此时,U=V。

此时,最小生成树构造完成!它包括的顶点依次是:A B F E D C G

普里姆算法的代码说明

以"邻接矩阵"为例对普里姆算法进行说明,对于"邻接表"实现的图在后面会给出相应的源码。

1. 基本定义

复制代码
// 邻接矩阵
typedef struct _graph
{
    char vexs[MAX];       // 顶点集合
    int vexnum;           // 顶点数
    int edgnum;           // 边数
    int matrix[MAX][MAX]; // 邻接矩阵
}Graph, *PGraph;

// 边的结构体
typedef struct _EdgeData
{
    char start; // 边的起点
    char end;   // 边的终点
    int weight; // 边的权重
}EData;
复制代码

Graph是邻接矩阵对应的结构体。 
vexs用于保存顶点,vexnum是顶点数,edgnum是边数;matrix则是用于保存矩阵信息的二维数组。例如,matrix[i][j]=1,则表示"顶点i(即vexs[i])"和"顶点j(即vexs[j])"是邻接点;matrix[i][j]=0,则表示它们不是邻接点。 
EData是邻接矩阵边对应的结构体。

2. 普里姆算法

复制代码
/*
 * prim最小生成树
 *
 * 参数说明:
 *       G -- 邻接矩阵图
 *   start -- 从图中的第start个元素开始,生成最小树
 */
void prim(Graph G, int start)
{
    int min,i,j,k,m,n,sum;
    int index=0;         // prim最小树的索引,即prims数组的索引
    char prims[MAX];     // prim最小树的结果数组
    int weights[MAX];    // 顶点间边的权值

    // prim最小生成树中第一个数是"图中第start个顶点",因为是从start开始的。
    prims[index++] = G.vexs[start];

    // 初始化"顶点的权值数组",
    // 将每个顶点的权值初始化为"第start个顶点"到"该顶点"的权值。
    for (i = 0; i < G.vexnum; i++ )
        weights[i] = G.matrix[start][i];
    // 将第start个顶点的权值初始化为0。
    // 可以理解为"第start个顶点到它自身的距离为0"。
    weights[start] = 0;

    for (i = 0; i < G.vexnum; i++)
    {
        // 由于从start开始的,因此不需要再对第start个顶点进行处理。
        if(start == i)
            continue;

        j = 0;
        k = 0;
        min = INF;
        // 在未被加入到最小生成树的顶点中,找出权值最小的顶点。
        while (j < G.vexnum)
        {
            // 若weights[j]=0,意味着"第j个节点已经被排序过"(或者说已经加入了最小生成树中)。
            if (weights[j] != 0 && weights[j] < min)
            {
                min = weights[j];
                k = j;
            }
            j++;
        }

        // 经过上面的处理后,在未被加入到最小生成树的顶点中,权值最小的顶点是第k个顶点。
        // 将第k个顶点加入到最小生成树的结果数组中
        prims[index++] = G.vexs[k];
        // 将"第k个顶点的权值"标记为0,意味着第k个顶点已经排序过了(或者说已经加入了最小树结果中)。
        weights[k] = 0;
        // 当第k个顶点被加入到最小生成树的结果数组中之后,更新其它顶点的权值。
        for (j = 0 ; j < G.vexnum; j++)
        {
            // 当第j个节点没有被处理,并且需要更新时才被更新。
            if (weights[j] != 0 && G.matrix[k][j] < weights[j])
                weights[j] = G.matrix[k][j];
        }
    }

    // 计算最小生成树的权值
    sum = 0;
    for (i = 1; i < index; i++)
    {
        min = INF;
        // 获取prims[i]在G中的位置
        n = get_position(G, prims[i]);
        // 在vexs[0...i]中,找出到j的权值最小的顶点。
        for (j = 0; j < i; j++)
        {
            m = get_position(G, prims[j]);
            if (G.matrix[m][n]<min)
                min = G.matrix[m][n];
        }
        sum += min;
    }
    // 打印最小生成树
    printf("PRIM(%c)=%d: ", G.vexs[start], sum);
    for (i = 0; i < index; i++)
        printf("%c ", prims[i]);
    printf("\n");
}
复制代码

普里姆算法的源码

这里分别给出"邻接矩阵图"和"邻接表图"的普里姆算法源码。

1邻接矩阵源码(matrix_udg.c)

2邻接表源码(list_udg.c)

 
 
/**
* C: prim算法生成最小生成树(邻接矩阵)
*
* @author skywang
* @date 2014/04/23
*/

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>

#define MAX 100 // 矩阵最大容量
#define INF (~(0x1<<31)) // 最大值(即0X7FFFFFFF)
#define isLetter(a) ((((a)>='a')&&((a)<='z')) || (((a)>='A')&&((a)<='Z')))
#define LENGTH(a) (sizeof(a)/sizeof(a[0]))

// 邻接矩阵
typedef struct _graph
{
     char vexs [ MAX ]; // 顶点集合
     int vexnum ; // 顶点数
     int edgnum ; // 边数
     int matrix [ MAX ][ MAX ]; // 邻接矩阵
} Graph , * PGraph ;

/*
* 返回ch在matrix矩阵中的位置
*/
static int get_position ( Graph g , char ch )
{
     int i ;
     for ( i = 0 ; i < g . vexnum ; i ++ )
         if ( g . vexs [ i ] == ch )
             return i ;
     return - 1 ;
}

/*
* 读取一个输入字符
*/
static char read_char ()
{
     char ch ;

     do {
         ch = getchar ();
     } while ( ! isLetter ( ch ));

     return ch ;
}

/*
* 创建图(自己输入)
*/
Graph * create_graph ()
{
     char c1 , c2 ;
     int v , e ;
     int i , j , weight , p1 , p2 ;
     Graph * pG ;
    
     // 输入"顶点数"和"边数"
     printf ( "input vertex number: " );
     scanf ( "%d" , & v );
     printf ( "input edge number: " );
     scanf ( "%d" , & e );
     if ( v < 1 || e < 1 || ( e > ( v * ( v - 1 ))))
     {
         printf ( "input error: invalid parameters! \n " );
         return NULL ;
     }
    
     if (( pG = ( Graph * ) malloc ( sizeof ( Graph ))) == NULL )
         return NULL ;
     memset ( pG , 0 , sizeof ( Graph ));

     // 初始化"顶点数"和"边数"
     pG -> vexnum = v ;
     pG -> edgnum = e ;
     // 初始化"顶点"
     for ( i = 0 ; i < pG -> vexnum ; i ++ )
     {
         printf ( "vertex(%d): " , i );
         pG -> vexs [ i ] = read_char ();
     }

     // 1. 初始化"边"的权值
     for ( i = 0 ; i < pG -> vexnum ; i ++ )
     {
         for ( j = 0 ; j < pG -> vexnum ; j ++ )
         {
             if ( i == j )
                 pG -> matrix [ i ][ j ] = 0 ;
             else
                 pG -> matrix [ i ][ j ] = INF ;
         }
     }
     // 2. 初始化"边"的权值: 根据用户的输入进行初始化
     for ( i = 0 ; i < pG -> edgnum ; i ++ )
     {
         // 读取边的起始顶点,结束顶点,权值
         printf ( "edge(%d):" , i );
         c1 = read_char ();
         c2 = read_char ();
         scanf ( "%d" , & weight );

         p1 = get_position ( * pG , c1 );
         p2 = get_position ( * pG , c2 );
         if ( p1 ==- 1 || p2 ==- 1 )
         {
             printf ( "input error: invalid edge! \n " );
             free ( pG );
             return NULL ;
         }

         pG -> matrix [ p1 ][ p2 ] = weight ;
         pG -> matrix [ p2 ][ p1 ] = weight ;
     }

     return pG ;
}

/*
* 创建图(用已提供的矩阵)
*/
Graph * create_example_graph ()
{
     char vexs [] = { 'A' , 'B' , 'C' , 'D' , 'E' , 'F' , 'G' };
     int matrix [][ 9 ] = {
              /*A*//*B*//*C*//*D*//*E*//*F*//*G*/
       /*A*/ { 0 , 12 , INF , INF , INF , 16 , 14 },
       /*B*/ { 12 , 0 , 10 , INF , INF , 7 , INF },
       /*C*/ { INF , 10 , 0 , 3 , 5 , 6 , INF },
       /*D*/ { INF , INF , 3 , 0 , 4 , INF , INF },
       /*E*/ { INF , INF , 5 , 4 , 0 , 2 , 8 },
       /*F*/ { 16 , 7 , 6 , INF , 2 , 0 , 9 },
       /*G*/ { 14 , INF , INF , INF , 8 , 9 , 0 }};
     int vlen = LENGTH ( vexs );
     int i , j ;
     Graph * pG ;
    
     // 输入"顶点数"和"边数"
     if (( pG = ( Graph * ) malloc ( sizeof ( Graph ))) == NULL )
         return NULL ;
     memset ( pG , 0 , sizeof ( Graph ));

     // 初始化"顶点数"
     pG -> vexnum = vlen ;
     // 初始化"顶点"
     for ( i = 0 ; i < pG -> vexnum ; i ++ )
         pG -> vexs [ i ] = vexs [ i ];

     // 初始化"边"
     for ( i = 0 ; i < pG -> vexnum ; i ++ )
         for ( j = 0 ; j < pG -> vexnum ; j ++ )
             pG -> matrix [ i ][ j ] = matrix [ i ][ j ];

     // 统计边的数目
     for ( i = 0 ; i < pG -> vexnum ; i ++ )
         for ( j = 0 ; j < pG -> vexnum ; j ++ )
             if ( i != j && pG -> matrix [ i ][ j ] != INF )
                 pG -> edgnum ++ ;
     pG -> edgnum /= 2 ;

     return pG ;
}

/*
* 返回顶点v的第一个邻接顶点的索引,失败则返回-1
*/
static int first_vertex ( Graph G , int v )
{
     int i ;

     if ( v < 0 || v > ( G . vexnum - 1 ))
         return - 1 ;

     for ( i = 0 ; i < G . vexnum ; i ++ )
         if ( G . matrix [ v ][ i ] != 0 && G . matrix [ v ][ i ] != INF )
             return i ;

     return - 1 ;
}

/*
* 返回顶点v相对于w的下一个邻接顶点的索引,失败则返回-1
*/
static int next_vertix ( Graph G , int v , int w )
{
     int i ;

     if ( v < 0 || v > ( G . vexnum - 1 ) || w < 0 || w > ( G . vexnum - 1 ))
         return - 1 ;

     for ( i = w + 1 ; i < G . vexnum ; i ++ )
         if ( G . matrix [ v ][ i ] != 0 && G . matrix [ v ][ i ] != INF )
             return i ;

     return - 1 ;
}

/*
* 深度优先搜索遍历图的递归实现
*/
static void DFS ( Graph G , int i , int * visited )
{
     int w ;

     visited [ i ] = 1 ;
     printf ( "%c " , G . vexs [ i ]);
     // 遍历该顶点的所有邻接顶点。若是没有访问过,那么继续往下走
     for ( w = first_vertex ( G , i ); w >= 0 ; w = next_vertix ( G , i , w ))
     {
         if ( ! visited [ w ])
             DFS ( G , w , visited );
     }
       
}

/*
* 深度优先搜索遍历图
*/
void DFSTraverse ( Graph G )
{
     int i ;
     int visited [ MAX ]; // 顶点访问标记

     // 初始化所有顶点都没有被访问
     for ( i = 0 ; i < G . vexnum ; i ++ )
         visited [ i ] = 0 ;

     printf ( "DFS: " );
     for ( i = 0 ; i < G . vexnum ; i ++ )
     {
         //printf("\n== LOOP(%d)\n", i);
         if ( ! visited [ i ])
             DFS ( G , i , visited );
     }
     printf ( " \n " );
}

/*
* 广度优先搜索(类似于树的层次遍历)
*/
void BFS ( Graph G )
{
     int head = 0 ;
     int rear = 0 ;
     int queue [ MAX ]; // 辅组队列
     int visited [ MAX ]; // 顶点访问标记
     int i , j , k ;

     for ( i = 0 ; i < G . vexnum ; i ++ )
         visited [ i ] = 0 ;

     printf ( "BFS: " );
     for ( i = 0 ; i < G . vexnum ; i ++ )
     {
         if ( ! visited [ i ])
         {
             visited [ i ] = 1 ;
             printf ( "%c " , G . vexs [ i ]);
             queue [ rear ++ ] = i ; // 入队列
         }
         while ( head != rear )
         {
             j = queue [ head ++ ]; // 出队列
             for ( k = first_vertex ( G , j ); k >= 0 ; k = next_vertix ( G , j , k )) //k是为访问的邻接顶点
             {
                 if ( ! visited [ k ])
                 {
                     visited [ k ] = 1 ;
                     printf ( "%c " , G . vexs [ k ]);
                     queue [ rear ++ ] = k ;
                 }
             }
         }
     }
     printf ( " \n " );
}

/*
* 打印矩阵队列图
*/
void print_graph ( Graph G )
{
     int i , j ;

     printf ( "Martix Graph: \n " );
     for ( i = 0 ; i < G . vexnum ; i ++ )
     {
         for ( j = 0 ; j < G . vexnum ; j ++ )
             printf ( "%10d " , G . matrix [ i ][ j ]);
         printf ( " \n " );
     }
}

/*
* prim最小生成树
*
* 参数说明:
* G -- 邻接矩阵图
* start -- 从图中的第start个元素开始,生成最小树
*/
void prim ( Graph G , int start )
{
     int min , i , j , k , m , n , sum ;
     int index = 0 ; // prim最小树的索引,即prims数组的索引
     char prims [ MAX ]; // prim最小树的结果数组
     int weights [ MAX ]; // 顶点间边的权值

     // prim最小生成树中第一个数是"图中第start个顶点",因为是从start开始的。
     prims [ index ++ ] = G . vexs [ start ];

     // 初始化"顶点的权值数组",
     // 将每个顶点的权值初始化为"第start个顶点"到"该顶点"的权值。
     for ( i = 0 ; i < G . vexnum ; i ++ )
         weights [ i ] = G . matrix [ start ][ i ];
     // 将第start个顶点的权值初始化为0。
     // 可以理解为"第start个顶点到它自身的距离为0"。
     weights [ start ] = 0 ;

     for ( i = 0 ; i < G . vexnum ; i ++ )
     {
         // 由于从start开始的,因此不需要再对第start个顶点进行处理。
         if ( start == i )
             continue ;

         j = 0 ;
         k = 0 ;
         min = INF ;
         // 在未被加入到最小生成树的顶点中,找出权值最小的顶点。
         while ( j < G . vexnum )
         {
             // 若weights[j]=0,意味着"第j个节点已经被排序过"(或者说已经加入了最小生成树中)。
             if ( weights [ j ] != 0 && weights [ j ] < min )
             {
                 min = weights [ j ];
                 k = j ;
             }
             j ++ ;
         }

         // 经过上面的处理后,在未被加入到最小生成树的顶点中,权值最小的顶点是第k个顶点。
         // 将第k个顶点加入到最小生成树的结果数组中
         prims [ index ++ ] = G . vexs [ k ];
         // 将"第k个顶点的权值"标记为0,意味着第k个顶点已经排序过了(或者说已经加入了最小树结果中)。
         weights [ k ] = 0 ;
         // 当第k个顶点被加入到最小生成树的结果数组中之后,更新其它顶点的权值。
         for ( j = 0 ; j < G . vexnum ; j ++ )
         {
             // 当第j个节点没有被处理,并且需要更新时才被更新。
             if ( weights [ j ] != 0 && G . matrix [ k ][ j ] < weights [ j ])
                 weights [ j ] = G . matrix [ k ][ j ];
         }
     }

     // 计算最小生成树的权值
     sum = 0 ;
     for ( i = 1 ; i < index ; i ++ )
     {
         min = INF ;
         // 获取prims[i]在G中的位置
         n = get_position ( G , prims [ i ]);
         // 在vexs[0...i]中,找出到j的权值最小的顶点。
         for ( j = 0 ; j < i ; j ++ )
         {
             m = get_position ( G , prims [ j ]);
             if ( G . matrix [ m ][ n ] < min )
                 min = G . matrix [ m ][ n ];
         }
         sum += min ;
     }
     // 打印最小生成树
     printf ( "PRIM(%c)=%d: " , G . vexs [ start ], sum );
     for ( i = 0 ; i < index ; i ++ )
         printf ( "%c " , prims [ i ]);
     printf ( " \n " );
}

void main ()
{
     Graph * pG ;

     // 自定义"图"(输入矩阵队列)
     //pG = create_graph();
     // 采用已有的"图"
     pG = create_example_graph ();

     //print_graph(*pG); // 打印图
     //DFSTraverse(*pG); // 深度优先遍历
     //BFS(*pG); // 广度优先遍历

     prim ( * pG , 0 ); // prim算法生成最小生成树
}

 
 
/**
* C: prim算法生成最小生成树(邻接表)
*
* @author skywang
* @date 2014/04/23
*/

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>

#define MAX 100
#define INF (~(0x1<<31)) // 最大值(即0X7FFFFFFF)
#define isLetter(a) ((((a)>='a')&&((a)<='z')) || (((a)>='A')&&((a)<='Z')))
#define LENGTH(a) (sizeof(a)/sizeof(a[0]))

// 邻接表中表对应的链表的顶点
typedef struct _ENode
{
     int ivex ; // 该边的顶点的位置
     int weight ; // 该边的权
     struct _ENode * next_edge ; // 指向下一条弧的指针
} ENode , * PENode ;

// 邻接表中表的顶点
typedef struct _VNode
{
     char data ; // 顶点信息
     ENode * first_edge ; // 指向第一条依附该顶点的弧
} VNode ;

// 邻接表
typedef struct _LGraph
{
     int vexnum ; // 图的顶点的数目
     int edgnum ; // 图的边的数目
     VNode vexs [ MAX ];
} LGraph ;

/*
* 返回ch在matrix矩阵中的位置
*/
static int get_position ( LGraph g , char ch )
{
     int i ;
     for ( i = 0 ; i < g . vexnum ; i ++ )
         if ( g . vexs [ i ]. data == ch )
             return i ;
     return - 1 ;
}

/*
* 读取一个输入字符
*/
static char read_char ()
{
     char ch ;

     do {
         ch = getchar ();
     } while ( ! isLetter ( ch ));

     return ch ;
}

/*
* 将node链接到list的末尾
*/
static void link_last ( ENode * list , ENode * node )
{
     ENode * p = list ;

     while ( p -> next_edge )
         p = p -> next_edge ;
     p -> next_edge = node ;
}

/*
* 创建邻接表对应的图(自己输入)
*/
LGraph * create_lgraph ()
{
     char c1 , c2 ;
     int v , e ;
     int i , p1 , p2 ;
     int weight ;
     ENode * node1 , * node2 ;
     LGraph * pG ;

     // 输入"顶点数"和"边数"
     printf ( "input vertex number: " );
     scanf ( "%d" , & v );
     printf ( "input edge number: " );
     scanf ( "%d" , & e );
     if ( v < 1 || e < 1 || ( e > ( v * ( v - 1 ))))
     {
         printf ( "input error: invalid parameters! \n " );
         return NULL ;
     }
 
     if (( pG = ( LGraph * ) malloc ( sizeof ( LGraph ))) == NULL )
         return NULL ;
     memset ( pG , 0 , sizeof ( LGraph ));

     // 初始化"顶点数"和"边数"
     pG -> vexnum = v ;
     pG -> edgnum = e ;
     // 初始化"邻接表"的顶点
     for ( i = 0 ; i < pG -> vexnum ; i ++ )
     {
         printf ( "vertex(%d): " , i );
         pG -> vexs [ i ]. data = read_char ();
         pG -> vexs [ i ]. first_edge = NULL ;
     }

     // 初始化"邻接表"的边
     for ( i = 0 ; i < pG -> edgnum ; i ++ )
     {
         // 读取边的起始顶点,结束顶点,权
         printf ( "edge(%d): " , i );
         c1 = read_char ();
         c2 = read_char ();
         scanf ( "%d" , & weight );

         p1 = get_position ( * pG , c1 );
         p2 = get_position ( * pG , c2 );

         // 初始化node1
         node1 = ( ENode * ) malloc ( sizeof ( ENode ));
         node1 -> ivex = p2 ;
         node1 -> weight = weight ;
         // 将node1链接到"p1所在链表的末尾"
         if ( pG -> vexs [ p1 ]. first_edge == NULL )
           pG -> vexs [ p1 ]. first_edge = node1 ;
         else
             link_last ( pG -> vexs [ p1 ]. first_edge , node1 );
         // 初始化node2
         node2 = ( ENode * ) malloc ( sizeof ( ENode ));
         node2 -> ivex = p1 ;
         node2 -> weight = weight ;
         // 将node2链接到"p2所在链表的末尾"
         if ( pG -> vexs [ p2 ]. first_edge == NULL )
             pG -> vexs [ p2 ]. first_edge = node2 ;
         else
             link_last ( pG -> vexs [ p2 ]. first_edge , node2 );
     }

     return pG ;
}

// 边的结构体(用来创建示例图)
typedef struct _edata
{
     char start ; // 边的起点
     char end ; // 边的终点
     int weight ; // 边的权重
} EData ;

// 顶点
static char gVexs [] = { 'A' , 'B' , 'C' , 'D' , 'E' , 'F' , 'G' };
// 边
static EData gEdges [] = {
   // 起点 终点 权
     { 'A' , 'B' , 12 },
     { 'A' , 'F' , 16 },
     { 'A' , 'G' , 14 },
     { 'B' , 'C' , 10 },
     { 'B' , 'F' , 7 },
     { 'C' , 'D' , 3 },
     { 'C' , 'E' , 5 },
     { 'C' , 'F' , 6 },
     { 'D' , 'E' , 4 },
     { 'E' , 'F' , 2 },
     { 'E' , 'G' , 8 },
     { 'F' , 'G' , 9 },
};

/*
* 创建邻接表对应的图(用已提供的数据)
*/
LGraph * create_example_lgraph ()
{
     char c1 , c2 ;
     int vlen = LENGTH ( gVexs );
     int elen = LENGTH ( gEdges );
     int i , p1 , p2 ;
     int weight ;
     ENode * node1 , * node2 ;
     LGraph * pG ;

     if (( pG = ( LGraph * ) malloc ( sizeof ( LGraph ))) == NULL )
         return NULL ;
     memset ( pG , 0 , sizeof ( LGraph ));

     // 初始化"顶点数"和"边数"
     pG -> vexnum = vlen ;
     pG -> edgnum = elen ;
     // 初始化"邻接表"的顶点
     for ( i = 0 ; i < pG -> vexnum ; i ++ )
     {
         pG -> vexs [ i ]. data = gVexs [ i ];
         pG -> vexs [ i ]. first_edge = NULL ;
     }

     // 初始化"邻接表"的边
     for ( i = 0 ; i < pG -> edgnum ; i ++ )
     {
         // 读取边的起始顶点,结束顶点,权
         c1 = gEdges [ i ]. start ;
         c2 = gEdges [ i ]. end ;
         weight = gEdges [ i ]. weight ;

         p1 = get_position ( * pG , c1 );
         p2 = get_position ( * pG , c2 );

         // 初始化node1
         node1 = ( ENode * ) malloc ( sizeof ( ENode ));
         node1 -> ivex = p2 ;
         node1 -> weight = weight ;
         // 将node1链接到"p1所在链表的末尾"
         if ( pG -> vexs [ p1 ]. first_edge == NULL )
             pG -> vexs [ p1 ]. first_edge = node1 ;
         else
             link_last ( pG -> vexs [ p1 ]. first_edge , node1 );
         // 初始化node2
         node2 = ( ENode * ) malloc ( sizeof ( ENode ));
         node2 -> ivex = p1 ;
         node2 -> weight = weight ;
         // 将node2链接到"p2所在链表的末尾"
         if ( pG -> vexs [ p2 ]. first_edge == NULL )
             pG -> vexs [ p2 ]. first_edge = node2 ;
         else
             link_last ( pG -> vexs [ p2 ]. first_edge , node2 );
     }

     return pG ;
}

/*
* 深度优先搜索遍历图的递归实现
*/
static void DFS ( LGraph G , int i , int * visited )
{
     int w ;
     ENode * node ;

     visited [ i ] = 1 ;
     printf ( "%c " , G . vexs [ i ]. data );
     node = G . vexs [ i ]. first_edge ;
     while ( node != NULL )
     {
         if ( ! visited [ node -> ivex ])
             DFS ( G , node -> ivex , visited );
         node = node -> next_edge ;
     }
}

/*
* 深度优先搜索遍历图
*/
void DFSTraverse ( LGraph G )
{
     int i ;
     int visited [ MAX ]; // 顶点访问标记

     // 初始化所有顶点都没有被访问
     for ( i = 0 ; i < G . vexnum ; i ++ )
         visited [ i ] = 0 ;

     printf ( "DFS: " );
     for ( i = 0 ; i < G . vexnum ; i ++ )
     {
         if ( ! visited [ i ])
             DFS ( G , i , visited );
     }
     printf ( " \n " );
}

/*
* 广度优先搜索(类似于树的层次遍历)
*/
void BFS ( LGraph G )
{
     int head = 0 ;
     int rear = 0 ;
     int queue [ MAX ]; // 辅组队列
     int visited [ MAX ]; // 顶点访问标记
     int i , j , k ;
     ENode * node ;

     for ( i = 0 ; i < G . vexnum ; i ++ )
         visited [ i ] = 0 ;

     printf ( "BFS: " );
     for ( i = 0 ; i < G . vexnum ; i ++ )
     {
         if ( ! visited [ i ])
         {
             visited [ i ] = 1 ;
             printf ( "%c " , G . vexs [ i ]. data );
             queue [ rear ++ ] = i ; // 入队列
         }
         while ( head != rear )
         {
             j = queue [ head ++ ]; // 出队列
             node = G . vexs [ j ]. first_edge ;
             while ( node != NULL )
             {
                 k = node -> ivex ;
                 if ( ! visited [ k ])
                 {
                     visited [ k ] = 1 ;
                     printf ( "%c " , G . vexs [ k ]. data );
                     queue [ rear ++ ] = k ;
                 }
                 node = node -> next_edge ;
             }
         }
     }
     printf ( " \n " );
}

/*
* 打印邻接表图
*/
void print_lgraph ( LGraph G )
{
     int i , j ;
     ENode * node ;

     printf ( "List Graph: \n " );
     for ( i = 0 ; i < G . vexnum ; i ++ )
     {
         printf ( "%d(%c): " , i , G . vexs [ i ]. data );
         node = G . vexs [ i ]. first_edge ;
         while ( node != NULL )
         {
             printf ( "%d(%c) " , node -> ivex , G . vexs [ node -> ivex ]. data );
             node = node -> next_edge ;
         }
         printf ( " \n " );
     }
}

/*
* 获取G中边<start, end>的权值;若start和end不是连通的,则返回无穷大。
*/
int getWeight ( LGraph G , int start , int end )
{
     ENode * node ;

     if ( start == end )
         return 0 ;

     node = G . vexs [ start ]. first_edge ;
     while ( node != NULL )
     {
         if ( end == node -> ivex )
             return node -> weight ;
         node = node -> next_edge ;
     }

     return INF ;
}

/*
* prim最小生成树
*
* 参数说明:
* G -- 邻接表图
* start -- 从图中的第start个元素开始,生成最小树
*/
void prim ( LGraph G , int start )
{
     int min , i , j , k , m , n , tmp , sum ;
     int index = 0 ; // prim最小树的索引,即prims数组的索引
     char prims [ MAX ]; // prim最小树的结果数组
     int weights [ MAX ]; // 顶点间边的权值

     // prim最小生成树中第一个数是"图中第start个顶点",因为是从start开始的。
     prims [ index ++ ] = G . vexs [ start ]. data ;

     // 初始化"顶点的权值数组",
     // 将每个顶点的权值初始化为"第start个顶点"到"该顶点"的权值。
     for ( i = 0 ; i < G . vexnum ; i ++ )
         weights [ i ] = getWeight ( G , start , i );

     for ( i = 0 ; i < G . vexnum ; i ++ )
     {
         // 由于从start开始的,因此不需要再对第start个顶点进行处理。
         if ( start == i )
             continue ;

         j = 0 ;
         k = 0 ;
         min = INF ;
         // 在未被加入到最小生成树的顶点中,找出权值最小的顶点。
         while ( j < G . vexnum )
         {
             // 若weights[j]=0,意味着"第j个节点已经被排序过"(或者说已经加入了最小生成树中)。
             if ( weights [ j ] != 0 && weights [ j ] < min )
             {
                 min = weights [ j ];
                 k = j ;
             }
             j ++ ;
         }

         // 经过上面的处理后,在未被加入到最小生成树的顶点中,权值最小的顶点是第k个顶点。
         // 将第k个顶点加入到最小生成树的结果数组中
         prims [ index ++ ] = G . vexs [ k ]. data ;
         // 将"第k个顶点的权值"标记为0,意味着第k个顶点已经排序过了(或者说已经加入了最小树结果中)。
         weights [ k ] = 0 ;
         // 当第k个顶点被加入到最小生成树的结果数组中之后,更新其它顶点的权值。
         for ( j = 0 ; j < G . vexnum ; j ++ )
         {
             // 获取第k个顶点到第j个顶点的权值
             tmp = getWeight ( G , k , j );
             // 当第j个节点没有被处理,并且需要更新时才被更新。
             if ( weights [ j ] != 0 && tmp < weights [ j ])
                 weights [ j ] = tmp ;
         }
     }

     // 计算最小生成树的权值
     sum = 0 ;
     for ( i = 1 ; i < index ; i ++ )
     {
         min = INF ;
         // 获取prims[i]在G中的位置
         n = get_position ( G , prims [ i ]);
         // 在vexs[0...i]中,找出到j的权值最小的顶点。
         for ( j = 0 ; j < i ; j ++ )
         {
             m = get_position ( G , prims [ j ]);
             tmp = getWeight ( G , m , n );
             if ( tmp < min )
                 min = tmp ;
         }
         sum += min ;
     }
     // 打印最小生成树
     printf ( "PRIM(%c)=%d: " , G . vexs [ start ]. data , sum );
     for ( i = 0 ; i < index ; i ++ )
         printf ( "%c " , prims [ i ]);
     printf ( " \n " );
}

void main ()
{
     LGraph * pG ;

     // 自定义"图"(自己输入数据)
     //pG = create_lgraph();
     // 采用已有的"图"
     pG = create_example_lgraph ();

     //print_lgraph(*pG); // 打印图
     //DFSTraverse(*pG); // 深度优先遍历
     //BFS(*pG); // 广度优先遍历
     prim ( * pG , 0 ); // prim算法生成最小生成树
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值