图——图的Prim法最小生成树实现

1,运营商的挑战:

1,在下图标出的城市间架设一条通信线路;

2,要求:

1,任意两个城市间都能够通信;

2,将架设成本降至最低;

2,问题抽象:

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

3,最小(大)生成树:

1,仅使用图中的 n - 1 条边连接图中的 n 个顶点;

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

3,各边上的权值总和达到最小(大);

4,寻找最小生成树:

5,使用 prim 方法手工寻找最小生成树:

6,最小生成树算法步骤(prim):

1,选择某一顶点 v0 作为起始顶点,使得 T = {v0},F = {v1, v2, ..., vn},E = {};

2,每次选择一条边,这条边是所有(u, v)中权值最小的边,且 u 属于 T,v 属于 F;

3,修改 T,F,E:

T = T + {v}, F = F - {v}, E = E + {(u, v)}

4,当 F != NULL 时,且(u, v)存在,转 2;否则,结束;

7,最小生成树算法的原材料:

1,如果 T 集合到 F 集合中同一个顶点的连接有多条,那么选取权值最小的连接;

8,最小生成树算法流程图:

9,注意事项:

1,最小生成树仅针对无向图有意义;

2,必须判断图对象是否能够看做无向图;

1,如果可以才能够用 prim 算法;

10,有向图可看做无向图的充分条件:

1,有向的任意两顶点之间若存在连接,则两顶点相互可达、权值相等;

11,图类型(Graph)中的新增成员函数:

1,virtual bool isAdjacent(int i, int j) = 0;

1,判断在当前图中顶点 i 到顶点 j 是否邻接;

2,bool asUndirected();

1,判断当前的有向图是否能够看做无向图;

12,最小生成树 prim 算法实现:

 1,判断邻接的实现:  1,邻接矩阵的实现:
1     /* 判断 i 到 j 顶点边是否连接,值不为空就连接 */
2     bool isAdjacent(int i, int j)
3     {
4         return (0 <= i) && (i < vCount()) && (0 <= j) && (j < vCount()) && (m_edges[i][j] != NULL);
5     }
  2,邻接链表的实现:
1     bool isAdjacent(int i, int j)
2     {
3         return (0 <= i) && (i < vCount()) && (0 <= j) && (j < vCount()) && (m_list.get(i)->edge.find(Edge<E>(i, j)) >= 0);
4     }

   2,判断是否为无向图的实现:

 1    /* 最小生成树只能用于无向图,而我们针对的是有向图,所以要判断有向图什么时候能够被当做无向图 */
 2     bool asUndirected()
 3     {
 4        bool ret = true;
 5
 6        for(int i=0; i<vCount(); i++)
 7        {
 8            for(int j=0; j<vCount(); j++)
 9            {
10                /* i 到 j 是连接的,并且 j 到 i 也是连接的,然后权值也要相等 */
11                if( isAdjacent(i, j) )
12                {
13                    ret = ret && isAdjacent(j, i)  && (getEdge(i, j) == getEdge(j, i));
14                }
15            }
16        }
17
18        return ret;
19     }

  3,Prim算法实现:

 1    /* Prim 法实现最小、大生成树;返回值是数组,因为最小、大生成树的结果就是一系列的边,所以返回边的数组;参数表示理论上的最大权值; */
 2     SharedPointer< Array< Edge<E> > > prim(const E& LIMIT, const bool MINIMUM = true)  // 返回一个指向存储边的数组指针
 3     {
 4         LinkQueue< Edge<E> > ret;  // 返回边队列,本质是 E 集合
 5
 6         /* 执行 prim 算法 */
 7         if( asUndirected() )  // 无向图
 8         {
 9             DynamicArray<int> adjVex(vCount());//保存最小权值的边的 F集合中顶点
10             DynamicArray<bool> mark(vCount());  // 保存 T 集合或者 F 集合的标记
11             DynamicArray<E> cost(vCount());  // 保存最小权值的顶点中 E 集合中顶点,寻找最小值要配合 mark 使用
12             SharedPointer< Array<int> > aj = NULL;  // 保存某个顶点邻接数组
13             bool end = false;  // 用于标记判断 prim 是否要中断执行
14             int v = 0;  // 代表习惯性的从 0 顶点生成最小生成树
15
16             /* 执行初始化 */
17             for(int i=0; i<vCount(); i++)
18             {
19                 adjVex[i] = -1;  // 没有边被访问
20                 mark[i] = false;  // 顶点都没有被访问
21                 cost[i] = LIMIT;  // 参数传递理论上的最大权值
22             }
23
24             mark[v] = true;  // 初始顶点做标记
25
26             aj = getAdgacent(v);  // 获取初始顶点的邻接顶点
27
28             /* 设置初始顶点对应的位置 */
29             for(int j=0; j<aj->length(); j++)
30             {
31                 cost[(*aj)[j]] = getEdge(v, (*aj)[j]);  // 保存/到对应顶点的响应权值
32                 adjVex[(*aj)[j]] = v;  // 记录权值所对应的顶点,即能够得到边
33             }
34
35             /* 真正循环找边 */
36             for(int i=0; (i<vCount()) && !end; i++)  // 最多循环顶点次;也可能条件不满足,提前结束,所以有 !end
37             {
38                 E m = LIMIT;
39                 int k = -1;  // 记录最小值的顶点
40
41                 /* 通过 cost 数组找最小值 */
42                 for(int j=0; j<vCount(); j++)
43                 {
44                     if( !mark[j] && (MINIMUM ? (cost[j] < m) : (cost[j] > m)) ) // !makr[j] 条件是因为选取最小权值时本质上是选取连接的最小边,对应的顶点是在 F集合,此时 mark 中对应的值为假当中的,则在 mark 数组中对应的就为假,所以要这个条件,这里有最小值最大值的设置
45           {
46                         m = cost[j];
47                         k = j;  // 得到记录的最小值的顶点号
48                     }
49                 }
50                 end = (k == -1);  // 是否找到合法最小权值,因为有可能在上面 if 条件中没有找到合法的最小权值
51                 if( !end )
52                 {
53                     ret.add(Edge<E>(adjVex[k], k, getEdge(adjVex[k], k)));  // 在 adjVex 中找到这条边
54
55                     mark[k] = true;  // 标记顶点进入了 T 集合
56
57                     aj = getAdgacent(k);  // 找新的集合连接
58
59                     /* 找到之后更新 cost 数组和 adgVex 数组 */
60                     for(int j=0; j<aj->length(); j++)
61                     {
62                         if( !mark[(*aj)[j]] && (MINIMUM ? (getEdge(k, (*aj)[j]) < cost[(*aj)[j]]) : (getEdge(k, (*aj)[j]) > cost[(*aj)[j]])) );  //只对 F 集合操作
63                         {
64                             cost[(*aj)[j]] = getEdge(k ,(*aj)[j]); //如果 T 到 F 集合新连接权值较小,则记录到 cost 数组中,新加入的点 k 和之前 T 集合里的点到 F 集合里的点的权值要比较呢;如果在 k 到 F 集合中找不到合适的点,则用T中的点代替
65                             adjVex[(*aj)[j]] = k;  // 将最小权值的起始点设入到邻接边中
66                         }
67                     }
68                 }
69             }
70         }
71         else
72         {
73             THROW_EXCEPTION(InvalidOperationException, "Prim operation is for undirected graph only ...");
74         }
75
76         /* 判断边的数目是否够,即 n-1 条边 */
77         if( ret.length() != (vCount() - 1) )
78         {
79             THROW_EXCEPTION(InvalidOperationException, "No enough edge for prim operation ...");
80         }
81         return toArray(ret);  // 返回值是边的数组
82     }

14,prim 算法测试代码:

 1 #include <iostream>
 2 #include "MatrixGraph.h"
 3 #include "ListGraph.h"
 4
 5 using namespace std;
 6 using namespace DTLib;
 7
 8 template< typename V, typename E >
 9 Graph<V, E>& GraphEasy()
10 {
11    static MatrixGraph<4, V, E> g;
12
13     g.setEdge(0, 1, 1);
14     g.setEdge(1, 0, 1);
15     g.setEdge(0, 2, 3);
16     g.setEdge(2, 0, 3);
17     g.setEdge(1, 2, 1);
18     g.setEdge(2, 1, 1);
19     g.setEdge(1, 3, 4);
20     g.setEdge(3, 1, 4);
21     g.setEdge(2, 3, 1);
22    g.setEdge(3, 2, 1);
23
24     return g;
25 }
26
27 template< typename V, typename E >
28 Graph<V, E>& GraphComplex()
29 {
30    static ListGraph<V, E> g(9);
31
32     g.setEdge(0, 1, 10);
33     g.setEdge(1, 0, 10);
34     g.setEdge(0, 5, 11);
35     g.setEdge(5, 0, 11);
36     g.setEdge(1, 2, 18);
37     g.setEdge(2, 1, 18);
38     g.setEdge(1, 8, 12);
39     g.setEdge(8, 1, 12);
40     g.setEdge(1, 6, 16);
41     g.setEdge(6, 1, 16);
42     g.setEdge(2, 3, 22);
43     g.setEdge(3, 2, 22);
44     g.setEdge(2, 8, 8);
45     g.setEdge(8, 2, 8);
46     g.setEdge(3, 8, 21);
47     g.setEdge(8, 3, 21);
48     g.setEdge(3, 6, 24);
49     g.setEdge(6, 3, 24);
50     g.setEdge(3, 7, 16);
51     g.setEdge(7, 3, 16);
52     g.setEdge(3, 4, 20);
53     g.setEdge(4, 3, 20);
54     g.setEdge(4, 5, 26);
55     g.setEdge(5, 4, 26);
56     g.setEdge(4, 7, 7);
57     g.setEdge(7, 4, 7);
58     g.setEdge(5, 6, 17);
59     g.setEdge(6, 5, 17);
60     g.setEdge(6, 7, 19);
61    g.setEdge(7, 6, 19);
62
63     return g;
64 }
65
66 int main()
67 {
68     Graph<int, int>& g = GraphEasy<int, int>();
69    SharedPointer< Array< Edge<int> > > sa = g.prim(65535);
70
71    int w = 0;
72
73     for(int i=0; i<sa->length(); i++)
74     {
75         w += (*sa)[i].data;
76
77         cout << (*sa)[i].b << " " << (*sa)[i].e << " " << (*sa)[i].data << endl;
78    }
79
80    cout << "Weight: " << w << endl;
81
82     return 0;
83 }

15,小结:

1,最小生成树使得顶点间的连通代价最小;

2,Prim 算法通过顶点的动态标记寻找最小生成树;

3,Prim 算法的关键是集合概念的运用(T 集合,F 集合);

4,利用 Prim 算法的思想也能寻找图的“最大生成树”;

原文地址:https://www.cnblogs.com/dishengAndziyu/p/10926547.html

时间: 2024-10-11 12:41:12

图——图的Prim法最小生成树实现的相关文章

图的基本算法(最小生成树)

假设以下情景,有一块木板.板上钉上了一些钉子.这些钉子能够由一些细绳连接起来.假设每一个钉子能够通过一根或者多根细绳连接起来.那么一定存在这种情况,即用最少的细绳把全部钉子连接起来. 更为实际的情景是这种情况.在某地分布着N个村庄.如今须要在N个村庄之间修路,每一个村庄之前的距离不同,问怎么修最短的路,将各个村庄连接起来. 以上这些问题都能够归纳为最小生成树问题,用正式的表述方法描写叙述为:给定一个无方向的带权图G=(V, E),最小生成树为集合T, T是以最小代价连接V中全部顶点所用边E的最小

看数据结构写代码(35) 图的邻接矩阵表示法

杂谈:最近清明小长假,好好的放松了一下.节前 和 节后 都有点 松懈.不好,不好.贵在坚持.加油. 图的邻接矩阵表示法是用 两个数组 来表示 图的数据结构.一个是顶点数组,另一个是邻接矩阵数组.邻接矩阵 里存放着 顶点的关系. 用邻接矩阵表示图,在 看 顶点之间 是否有边,或者 求顶点的度等操作时比较简单.但空间浪费巨大,在插入,删除 顶点 和边 操作时 需要 移动大量数据,造成不便.所以在插入删除比较多,节点数比较多的时候 不宜 使用这种结构. 下面上代码: 源代码网盘地址:点击打开链接 //

数据结构--图--图的数组存储表示,深度优先搜索遍历和广度优先搜索遍历

图有四种存储结构:数组,邻接表,十字链表,邻接多重表.下面以数组为存储结构来实现图的深度优先搜索遍历和广度优先搜索遍历.其中广度优先搜索遍历中有用到STL中的queue,注意头文件的包含.具体代码如下: //图的数组(邻接矩阵)存储表示和深度优先遍历 const int MAX_VERTEX_NUM=20; //最大顶点数 typedef enum {DG,DN,UDG,UDN} GraphKind ;//(有向图,有向网,无向图,无向网) typedef int VRType; typedef

随机Prim法创建随机迷宫(C#实现)

因为这两天想参加一个比赛,所以就在上网找素材,刚好看到了迷宫生成,就决定拿这个开刀了. 参考的原文地址为(来源页面) 源地址中是使用AS实现的,没学过AS,所以直接不会运行,于是就自己根据原文的概念进行了模(chao)仿(xi). 废话说完了,现在来说一下随机Prim法的原理: 1.建立两个数组,一个是用于存储地图的二维数组α,另一个是用于存储待处理的墙的数组β. 2.将α的所有方格全部初始化为墙. 3.选定起点,并将该位置的墙变为路,将其四周的四块置入β数组中(出界的直接筛掉,就不说了). 4

给他任何一套图图图u又哭了

夫人和翻译秃头一天又图图图5634564646464合格合格合格和非官方的活动符合东方给他任何一套图图图u又哭了给他任何一套图图图u又哭了给他任何一套图图图u又哭了给他任何一套图图图u又哭了给他任何一套图图图u又哭了给他任何一套图图图u又哭了给他任何一套图图图u又哭了给他任何一套图图图u又哭了给他任何一套图图图u又哭了给他任何一套图图图u又哭了给他任何一套图图图u又哭了给他任何一套图图图u又哭了给他任何一套图图图u又哭了给他任何一套图图图u又哭了给他任何一套图图图u又哭了给他任何一套图图图u又哭

组队赛第五场 组合隔板法+最小生成树预处理并查集

UVALive 6434 题目链接:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4445 这题正好就是大一小学期的时候好像是曦曦出的那个牛那题吧,就是要买多少块板然后正好把牛给拦完.这题也是一样,就是找最大的距离,当做隔板,依次把最大的距离去掉,最后的就是最小的了. #include<iostream> #in

15 图-图的遍历-基于邻接矩阵实现的BFS与DFS算法

算法分析和具体步骤解说直接写在代码注释上了 TvT 没时间了等下还要去洗衣服 就先不赘述了 有不明白的欢迎留言交流!(估计是没人看的了) 直接上代码: 1 #include<stdio.h> 2 #include<queue> 3 #include<iostream> 4 using namespace std; 5 typedef struct{ 6 int Vex[10];//顶点表 7 int Edge[10][10]; 8 int vexnum,arcnum;

UOJ #589. 图图的游戏

[题目描述]: 图图正在玩一个智力游戏:有一个n×n 的01 方格,图图要从中选出一个面积最大的矩形区域,要求这个矩形区域不能有超过k个1. 这么难的问题图图当然不会做了,他想让你帮帮他,你能解决这个问题吗? [输入描述]: 第一行包含2 个正整数n,k. 接下来n 行每行n 个整数,表示这个01方格. [输出描述]: 输出1 个整数,表示最大面积. [样例输入]: 5 4 1 0 1 0 1 0 1 0 0 0 1 0 1 0 0 1 1 1 1 1 0 0 1 0 1 [样例输出]: 12

#589. 图图的游戏

[题目描述]: 图图正在玩一个智力游戏:有一个n×n 的01 方格,图图要从中选出一个面积最大的矩形区域,要求这个矩形区域不能有超过k个1. 这么难的问题图图当然不会做了,他想让你帮帮他,你能解决这个问题吗? [输入描述]: 第一行包含2 个正整数n,k. 接下来n 行每行n 个整数,表示这个01方格. [输出描述]: 输出1 个整数,表示最大面积. [样例输入]: 5 4 1 0 1 0 1 0 1 0 0 0 1 0 1 0 0 1 1 1 1 1 0 0 1 0 1 [样例输出]: 12