数据--第44课 - 最小连通网

第44课 - 最小连通网

1. 运营商的挑战

在下图标出的城市之间架设一条通信线路。

要求:任意两个城市间能够通信,将架设成本降至最低。

 

2. 问题的提出

如何在图中选择n-1条表使得n个顶点间两两可达,使得这n-1条边的权值之和最小。

(1)      必须使用且仅使用该网络中的n-1条边来连接网络中的n个结点;

(2)      不能使用产生回路的边;

(3)      各个边上的权值的总和达到最小。

 

3. Prim算法

从图N = {V, E}中选择某一个顶点u0进行标记,之后选择与它关联的具有最小权值的边(u0, v),并且将顶点v进行标记。

反复在一个顶点被标记,而在另一个顶点未被标记的各条边中选择权值最小的边(u0, v),并将为标记的顶点进行标记。

 

4. 算法步骤

(1)      从某一个顶点u0出发,使得U = {u0},TE={}

(2)      每次选择一条边,这条边是所有(u, v)中权值最小的边,而且,。修改U和TE:,。

(3)      当时,转2,;否则,结束。

 

5. 程序—Prim算法的实现

#include <stdio.h>

#include <stdlib.h>

 

/* run this program using the console pauser or add your own getch, system("pause") or input loop */

 

#define VNUM 9

#define MV 65536

 

int P[VNUM];

int Cost[VNUM];

int Mark[VNUM];

int Matrix[VNUM][VNUM] =

{

    {0, 10, MV, MV, MV, 11, MV, MV, MV},

    {10, 0, 18, MV, MV, MV, 16, MV, 12},

    {MV, 18, 0, 22, MV, MV, MV, MV, 8},

    {MV, MV, 22, 0, 20, MV, MV, 16, 21},

    {MV, MV, MV, 20, 0, 26, MV, 7, MV},

    {11, MV, MV, MV, 26, 0, 17, MV, MV},

    {MV, 16, MV, MV, MV, 17, 0, 19, MV},

    {MV, MV, MV, 16, 7, MV, 19, 0, MV},

    {MV, 12, 8, 21, MV, MV, MV, MV, 0},

};

 

void Prim(int sv) // O(n*n)

{

    int i = 0;

    int j = 0;

   

    if( (0 <= sv) && (sv < VNUM) )

    {

        for(i=0; i<VNUM; i++)

        {

            Cost[i] = Matrix[sv][i];

            P[i] = sv;

            Mark[i] = 0;

        }

       

        Mark[sv] = 1;

       

        for(i=0; i<VNUM; i++)

        {

            int min = MV;

            int index = -1;

           

            for(j=0; j<VNUM; j++)

            {

                if( !Mark[j] && (Cost[j] < min) )

                {

                    min = Cost[j];

                    index = j;

                }

            }

           

            if( index > -1 )

            {

                Mark[index] = 1;

               

                printf("(%d, %d, %d)\n", P[index], index, Cost[index]);

            }

           

            for(j=0; j<VNUM; j++)

            {

                if( !Mark[j] && (Matrix[index][j] < Cost[j]) )

                {

                    Cost[j]  = Matrix[index][j];

                    P[j] = index;

                }

            }

        }

    }

}

 

int main(int argc, char *argv[])

{

    Prim(0);

   

         return 0;

}

 

思考:

既然最小联通网是以边的权值之和为最终目标,那么是不是可以直接选择边,而不是通过顶点来选择边呢?

 

 

6. Kruskal算法

(1)      对于n个顶点的图G = {V, E}。

(2)      构造一个只有n个顶点,没有边的图。

(3)      在E中选择一条具有最小权值的边,若该边的两个顶点不构成回路,则将此边加入到T中;否则将此边社区,重新选择一条权值最小的边。

(4)      如此重复吓阻,直到所有顶点都联通位置。

 

7. Kruskal算法实现

步骤1:

定义边结构体        

typedef struct _tag_Edge

{

int begin;

int end;

int weight;

}TEdge;

 

步骤2:

定义边集数组并排序

 

begin

end

weight

edges[0]

4

7

7

edges[1]

2

8

8

edges[2]

0

1

10

edges[3]

0

5

11

edges[4]

1

8

12

edges[5]

3

7

16

edges[6]

1

6

16

edges[7]

5

6

17

edges[8]

1

2

18

edges[9]

6

7

19

edges[10]

3

4

20

edges[11]

3

8

21

edges[12]

2

3

22

edges[13]

3

6

24

edges[14]

4

5

26

 

步骤3:

定义辅助数组P[n],其中n为顶点数目。

P[]用于记录边顶点的首位连接关系。

 

8. Kruskal算法核心思想

遍历edges数组中的每一个元素。

通过P数组查找begin顶点的最终连接点v1

通过P数组查找end顶点的最终连接点v2.

v1 != v2则:当前边为最小连通网中的边,记录连接关系,P[v1] = v2;

v1 == v2则:产生回路,舍弃当前边。

算法举例:

 

 

小结:

(1)      Prim算法是针对顶点展开的,适合于边的数量较,适合于边的数量较多的情况。

(2)      Kruskal算法是针对边展开的,适合于边的数量较,适合于边的数量较少的情况。

posted @ 2019-08-11 18:49  free-锻炼身体  阅读(183)  评论(0编辑  收藏  举报