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

C语言-最小生成树(Prim算法) 最小代价生成树 最小生成树算法: 普里姆算法(Prim):对顶点 克鲁斯卡尔算法(Kruskal):对边 普里姆算法: 阅读详情

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

}

普里姆算法c语言实现 将选中的顶点v标记为已访问过,并更新与v相邻的顶点到MST的距离。如果某个相邻顶点的距离小于当前该顶点到MST的距离,则更新该顶点到MST的距离,并将该顶点的父节点设置为v。从这些未被访问的顶点中选择一个距离最小的顶点可以通过遍历所有未被访问的顶点并更新它们的距离来实现。输入邻接矩阵表示的图,邻接矩阵是一个n×n的二维数组,其中graph[i][j]表示顶点i和顶点j之间的边的权值,0表示两个顶点之间没有边。初始化时,将所有顶点到MST的距离设置为无穷大,除了起点到MST的距离设置为0。 阅读详情

相关推荐

数据结构笔记(一)—— C语言实现邻接矩阵存储的无向图,判断是否为连通图,并且实现最小生成树Prim算法

使用C语言实现C语言实现邻接矩阵存储的无向图,判断是否为连通图,并且实现最小生成树Prim算法。涉及到并查集、堆排序。

bobodesu的博客 3152

图之最小生成树Prim算法详解(C语言版)

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

qq_44075108的博客 9480

C 语言 随机prim算法 生成迷宫

C 语言 随机prim算法 生成迷宫。

奇点.的博客 4924

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

我们可以将定义理解为:将图的所有顶点分为两类,A类(保存已经查找过的顶点),B类(保存未查找过的顶点),从任一顶点开始,并将其从B类移至A类,然后开始寻找B类中(的顶点)到A类顶点之间权值最小的顶点,将其从B类中移至A类,重复上述操作,直至B类中没有顶点。所走过的顶点和边就是该连通图的最小生成树。

m0_62341092的博客 1万+

C/C++ prim算法求加权连通图里搜索最小生成树算法详解及源码

Prim算法是一种用于求解加权连通图中搜索最小生成树的算法。它的基本思想是从图中的一个顶点开始,逐步扩展最小生成树的边,直到覆盖所有的顶点。

希望我的博客,能帮上你解决学习中工作中所遇到的问题 6835

【2024年华为OD机试】 (A卷,200分)- 最优高铁城市修建方案(JavaScript&Java & Python&C/C++)

华为OD(Outsourcing Developer,外包开发工程师)是华为针对软件开发工程师岗位的一种招聘形式,主要包括笔试、技术面试以及综合面试等环节。尤其在笔试部分,算法题的机试至关重要。

嵌入式技术开发 904

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

本章介绍普里姆算法。和以往一样,本文会先对普里姆算法的理论论知识进行介绍,然后给出C语言的实现。后续再分别给出C++和Java版本的实现。普里姆算法介绍普里姆(Prim)算法,和克鲁斯卡尔算法一样,是用来求加权连通图的最小生成树的算法。基本思想对于图G而言,V是所有顶点的集合;现在,设置两个新的集合U和T,其中U用于存放G的最小生成树中的顶点,T存放G的最小生成树中的边。从所有u?U,v?(V-U...

weixin_39743622的博客 1161

Prim(普里姆)算法求最小生成树的思想及C语言实例讲解

Prim 算法思想: 从任意一顶点 v0 开始选择其最近顶点 v1 构成树 T1,再连接与 T1 最近顶点 v2 构成树 T2, 如此重复直到所有顶点均在所构成树中为止。 最小生成树(MST):权值最小的生成树。 生成树和最小生成树的应用:要连通n个城市需要n-1条边线路。可以把边上的权值解释为线路的造价。则最小生成树表示使其造价最小的生成树。 构造网的最小生成树必须解决下面两个问题: 1、尽可能选取权值小的边,但不能构成回路; 2、选取n-1条恰当的边以连通n个顶点; MST性质:假设G=(V,E)是一个连通网,U是顶点V的一个非空子集。若(u,v)是一条具有最小权值的边,其中u∈U,v∈V

拓扑排序(一)之 C语言详解6,Prim算法(一)之 C语言详解

本章介绍普里姆算法。和以往一样,本文会先对普里姆算法的理论论知识进行介绍,然后给出C语言的实现。后续再分别给出C++和Java版本的实现。普里姆算法介绍普里姆(Prim)算法,和克鲁斯卡尔算法一样,是用来求加权连通图的最小生成树的算法。基本思想对于图G而言,V是所有顶点的集合;现在,设置两个新的集合U和T,其中U用于存放G的最小生成树中的顶点,T存放G的最小生成树中的边。从所有uЄU,vЄ(V-U...

weixin_34533343的博客 554

Verilog实现FIR滤波器

1、FIR滤波器简介 不追究FIR滤波器的深层含义,我们只关注如何实现;可以看出,FIR滤波器的本质就是延迟、系数相乘与求和,如下图: 2、设计 基于以上分析,按照三级流水实现FIR滤波器设计:信号延迟-系数相乘-求和 `timescale 1ns / 1ps module fir( input clk, input rst_n, input [3:0]din, output reg [9:0]dout ); // data reg reg [3:0]din1; reg [3:.

CLL_caicai的博客 1万+

空心杯电机在美容仪器中的应用:为什么你的洗脸刷动力这么强?

本文深入解析了空心杯电机如何成为高端美容仪器的核心动力源。通过对比传统电机,揭示了其无铁芯、低惯量、零齿槽效应的技术优势,从而实现了强劲动力、极致静音与细腻震动的用户体验。文章详细阐述了空心杯电机在洁面刷、导入仪等美容仪器中的具体应用与选型考量,并指出了开发中驱动匹配、测试等关键实战要点。

j2k3l4的博客 1013

堆优化prim算法C语言,最小生成树(二)--prim算法实现以及堆优化

一、最小生成树---prim算法实现思想:1、从任意一个顶点开始构造生成树,假设就从1号顶点吧,首先将顶点1加入生成树中,用一个一维数组book来标记哪些顶点已经加入了生成树。2、用数组dis记录生成树到各个顶点的距离,最初生成树中之后1号顶点,有直连边时,数组dis中存储的就是1号顶点到该顶点的边的权值,没有直连边的时候就是无穷大,即初始化dis数组。3、从数组dis中选出离生成树最近的...

weixin_33268861的博客 1061

用逻辑分析仪实测EM4095:125kHz RFID耳标解码的电压与脉宽玄学

本文通过逻辑分析仪实测EM4095芯片,深入解析125kHz RFID耳标解码中的电压与脉宽关联现象。揭示了供电电压对曼彻斯特编码脉宽的影响规律,并提供了一套可落地的调试方法论,包括信号链路解析、逻辑分析仪配置技巧及系统级稳定性提升方案,帮助开发者解决解码不稳定问题。

weixin_30629653的博客 329

hdu1162 Eddy's picture (最小生成树之prim 算法)

Problem Description Eddy begins to like painting pictures recently ,he is sure of himself to become a painter.Every day Eddy draws pictures in his small room, and he usually puts out his newest pictu

青山绿水之辈 专栏 1228

喷雾破碎模型解析:从理论到CFD应用实践

本文深入解析喷雾破碎模型的理论基础与CFD应用实践,重点介绍KHRT、TAB和ETAB等主流模型的核心原理与工程调参技巧。通过OpenFOAM实例演示和参数优化建议,帮助工程师精准预测液滴破碎行为,提升燃烧效率与排放控制性能,适用于汽车发动机、燃气轮机等工业场景。

weixin_29281915的博客 499

Matlab实战:手把手教你用Zernike多项式模拟光学像差(附完整代码)

本文详细介绍了如何在Matlab中使用Zernike多项式模拟光学像差,包括基础理论、不同排序系统的实现、完整代码示例以及高级应用技巧。通过Zernike多项式,可以有效地描述和分析光学系统中的波前畸变,适用于望远镜校正、眼科光学等多个领域。

weixin_42523720的博客 161

poj 1287(最小生成树prime算法)

Networking Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 10475   Accepted: 5696 Description You are assigned to design network connections between certain

王纪刚的博客 333

多模态论文笔记——VQ-VAE和VQ-VAE-2

本文详细介绍VQ-VAE和VQ-VAE-2的原理和训练过程,为后面的dVAE在DALLE中的使用打下坚实的基础。

haopinglianlian的博客 3299

最小生成树 prim----------------

/*最小生成树,用Prim算法使用BZ数组*/ #include<stdio.h>#define INF 200int g[110][110],bz[110],low[110];int min,ans,Q,n,a,b; void prim(){ int i,j,k,x; for (i=1;i<=n;i++) { low[i]=g[1][i]; ...

weixin_30480075的博客 73

【FF-A】第二章 Introduction

Armv8.4 架构引入了在安全状态下的虚拟化扩展。Arm® SMMU v3.2 架构[1]为安全流添加了对第2阶段翻译的支持,以补充在 Armv8.4 处理器状态下的安全 EL2 翻译制度。这些架构特性使得在安全状态下,彼此不信任的软件组件能够相互隔离。关于在安全状态下引入虚拟化扩展以及固件框架目标的更多理念可在名为《在安全世界使用虚拟化进行隔离》的白皮书[3]中找到。软件组件只能访问物理地址空间和系统资源的区域,例如 GIC 中对其正确运行必要的中断。

baron-周贺贺-代码改变世界ctw 528

【推荐100个unity插件】unity实现交互式火焰 —— Ignis

Ignis是一款Unity互动式消防系统插件,可轻松将物体转换为可燃物。支持固体、植被、蒙皮网格和地形树木等多种对象类型,提供火焰蔓延、熄灭等效果。安装需通过Unity Package Manager添加Visual Effects Graph,并按照指引转换目标物体材质。使用盒型碰撞体或网格计算燃烧区域,可自定义火焰参数和视觉效果。内置多种转换选项,如Flammable Vegetation、Flammable Skinned Mesh等,适用于不同场景需求。注意使用前需备份材质和地形数据,并提供性能优化

向宇的博客,专注php/web全栈 unity游戏开发,欢迎大家评论纠错 2237

C语言实现Prim算法 —构建最小生成树

介绍Prim算法前的相关概念 一、Prim算法的思想 二.1、利用图形详细解释Prim算法的思想 二.2利用图形又又解释Prim算法的思想 三、用图示结合代码中重要量进行说明 四、代码实现(用c语言)

weixin_52375817的博客 1万+

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

C语言 Prim算法和Kruskal算法的实现和证明

最小生成树简介 原理 Prim算法 算法实现 算法证明 Kruskal算法 算法实现 算法证明 最小生成树简介 最小生成树(MST):给定一加权无向图,找出它的一颗最小生成树。 定义:图的最小生成树是它的一副含有其所有顶点的无环连通子图。一副加权图的最小生成树是它的一颗权值(树种所有边的权值之和)最小的生成树。 我们约定: 1. 只考虑连通...

fpk2014的博客 4107

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

C语言经典算法之普里姆(prim)算法

在C语言中实现普利姆(Prim)算法通常涉及数据结构的选择(如邻接矩阵或邻接表)、优先队列来存储待处理边以及变量来追踪已加入最小生成树的顶点。

weixin_56154577的博客 4577

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

使用 Prim 算法构建最小生成树:C 语言实现详解

最小生成树(Minimum Spanning Tree, MST)是图论中的一个重要概念,指的是在一个无向连通图中,连接所有顶点的边的集合,使得这些边的总权重最小,并且没有环路。最小生成树的存在和性质使其在许多实际应用中具有重要意义。在图论中,一个图由顶点(节点)和边(连接顶点的线段)组成。连通性:最小生成树必须连接图中的所有顶点。无环性:最小生成树不能包含环路,即没有顶点重复连接。边的权重最小:最小生成树的所有边的权重之和是所有可能生成树中最小的。

不迁怒,不贰过。小知识,大智慧。 1408
上一篇: 快速幂 与矩阵快速幂
下一篇: 欧拉回路
KLFTESPACE
博客等级 码龄9年 66粉丝 655原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值