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

转自sktywang12345

 

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

本章介绍普里姆算法。和以往一样,本文会先对普里姆算法的理论论知识进行介绍,然后给出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. 基本定义

复制代码

 
  1. // 邻接矩阵

  2. typedef struct _graph

  3. {

  4. char vexs[MAX]; // 顶点集合

  5. int vexnum; // 顶点数

  6. int edgnum; // 边数

  7. int matrix[MAX][MAX]; // 邻接矩阵

  8. }Graph, *PGraph;

  9.  
  10. // 边的结构体

  11. typedef struct _EdgeData

  12. {

  13. char start; // 边的起点

  14. char end; // 边的终点

  15. int weight; // 边的权重

  16. }EData;

复制代码

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

2. 普里姆算法

复制代码

 
  1. /*

  2. * prim最小生成树

  3. *

  4. * 参数说明:

  5. * G -- 邻接矩阵图

  6. * start -- 从图中的第start个元素开始,生成最小树

  7. */

  8. void prim(Graph G, int start)

  9. {

  10. int min,i,j,k,m,n,sum;

  11. int index=0; // prim最小树的索引,即prims数组的索引

  12. char prims[MAX]; // prim最小树的结果数组

  13. int weights[MAX]; // 顶点间边的权值

  14.  
  15. // prim最小生成树中第一个数是"图中第start个顶点",因为是从start开始的。

  16. prims[index++] = G.vexs[start];

  17.  
  18. // 初始化"顶点的权值数组",

  19. // 将每个顶点的权值初始化为"第start个顶点"到"该顶点"的权值。

  20. for (i = 0; i < G.vexnum; i++ )

  21. weights[i] = G.matrix[start][i];

  22. // 将第start个顶点的权值初始化为0。

  23. // 可以理解为"第start个顶点到它自身的距离为0"。

  24. weights[start] = 0;

  25.  
  26. for (i = 0; i < G.vexnum; i++)

  27. {

  28. // 由于从start开始的,因此不需要再对第start个顶点进行处理。

  29. if(start == i)

  30. continue;

  31.  
  32. j = 0;

  33. k = 0;

  34. min = INF;

  35. // 在未被加入到最小生成树的顶点中,找出权值最小的顶点。

  36. while (j < G.vexnum)

  37. {

  38. // 若weights[j]=0,意味着"第j个节点已经被排序过"(或者说已经加入了最小生成树中)。

  39. if (weights[j] != 0 && weights[j] < min)

  40. {

  41. min = weights[j];

  42. k = j;

  43. }

  44. j++;

  45. }

  46.  
  47. // 经过上面的处理后,在未被加入到最小生成树的顶点中,权值最小的顶点是第k个顶点。

  48. // 将第k个顶点加入到最小生成树的结果数组中

  49. prims[index++] = G.vexs[k];

  50. // 将"第k个顶点的权值"标记为0,意味着第k个顶点已经排序过了(或者说已经加入了最小树结果中)。

  51. weights[k] = 0;

  52. // 当第k个顶点被加入到最小生成树的结果数组中之后,更新其它顶点的权值。

  53. for (j = 0 ; j < G.vexnum; j++)

  54. {

  55. // 当第j个节点没有被处理,并且需要更新时才被更新。

  56. if (weights[j] != 0 && G.matrix[k][j] < weights[j])

  57. weights[j] = G.matrix[k][j];

  58. }

  59. }

  60.  
  61. // 计算最小生成树的权值

  62. sum = 0;

  63. for (i = 1; i < index; i++)

  64. {

  65. min = INF;

  66. // 获取prims[i]在G中的位置

  67. n = get_position(G, prims[i]);

  68. // 在vexs[0...i]中,找出到j的权值最小的顶点。

  69. for (j = 0; j < i; j++)

  70. {

  71. m = get_position(G, prims[j]);

  72. if (G.matrix[m][n]<min)

  73. min = G.matrix[m][n];

  74. }

  75. sum += min;

  76. }

  77. // 打印最小生成树

  78. printf("PRIM(%c)=%d: ", G.vexs[start], sum);

  79. for (i = 0; i < index; i++)

  80. printf("%c ", prims[i]);

  81. printf("\n");

  82. }

复制代码

普里姆算法的源码

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

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算法生成最小生成树

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值