最小生成树 红太狼 2022-05-24 11:41 271阅读 0赞 设G = (V,E)是无向连通带权图,即一个网络。E中的每一条边(v,w)的权为c\[v\]\[w\]。如果G的子图G’是一棵包含G的所有顶点的树,则称G’为G的生成树。生成树上各边权的总和称为生成树的耗费。在G的所有生成树中,耗费最小的生成树称为G的最小生成树。构造最小生成树的两种方法:Prim算法和Kruskal算法。 ## 一、最小生成树的性质 ## 设G = (V,E)是连通带权图,U是V的真子集。如果(u,v)∈E,且u∈U,v∈V-U,且在所有这样的边中,(u,v)的权c\[u\]\[v\]最小,那么一定存在G的一棵最小生成树,它意(u,v)为其中一条边。这个性质有时也称为MST性质。 ## 二、Prim算法 ## 设G = (V,E)是连通带权图,V = \{1,2,…,n\}。构造G的最小生成树Prim算法的基本思想是:首先置S = \{1\},然后,只要S是V的真子集,就进行如下的贪心选择:选取满足条件i ∈S,j ∈V – S,且c\[i\]\[j\]最小的边,将顶点j添加到S中。这个过程一直进行到S = V时为止。在这个过程中选取到的所有边恰好构成G的一棵最小生成树。 如下带权图: ![2010120212285861.jpg][] 生成过程: 1 -> 3 : 1 3 -> 6 : 4 6 -> 4: 2 3 -> 2 : 5 2 -> 5 : 3 prim算法的演示如下: [http://sjjp.tjuci.edu.cn/sjjg/DataStructure/DS/web/flashhtml/prim.htm][http_sjjp.tjuci.edu.cn_sjjg_DataStructure_DS_web_flashhtml_prim.htm] /* 主题:贪心算法——最小生成树(Prim) * 作者:chinazhangjie * 邮箱:chinajiezhang@gmail.com * 开发语言: C++ * 开发环境: Virsual Studio 2005 * 时间: 2010.11.30 */ #include <iostream> #include <vector> #include <limits> using namespace std ; struct TreeNode { public: TreeNode (int nVertexIndexA = 0, int nVertexIndexB = 0, int nWeight = 0) : m_nVertexIndexA (nVertexIndexA), m_nVertexIndexB (nVertexIndexB), m_nWeight (nWeight) { } public: int m_nVertexIndexA ; int m_nVertexIndexB ; int m_nWeight ; } ; class MST_Prim { public: MST_Prim (const vector<vector<int> >& vnGraph) { m_nvGraph = vnGraph ; m_nNodeCount = (int)m_nvGraph.size () ; } void DoPrim () { // 是否被访问标志 vector<bool> bFlag (m_nNodeCount, false) ; bFlag[0] = true ; int nMaxIndexA ; int nMaxIndexB ; int j = 0 ; while (j < m_nNodeCount - 1) { int nMaxWeight = numeric_limits<int>::max () ; // 找到当前最短路径 int i = 0 ; while (i < m_nNodeCount) { if (!bFlag[i]) { ++ i ; continue ; } for (int j = 0; j < m_nNodeCount; ++ j) { if (!bFlag[j] && nMaxWeight > m_nvGraph[i][j]) { nMaxWeight = m_nvGraph[i][j] ; nMaxIndexA = i ; nMaxIndexB = j ; } } ++ i ; } bFlag[nMaxIndexB] = true ; m_tnMSTree.push_back (TreeNode(nMaxIndexA, nMaxIndexB, nMaxWeight)) ; ++ j ; } // 输出结果 for (vector<TreeNode>::const_iterator ite = m_tnMSTree.begin() ; ite != m_tnMSTree.end() ; ++ ite ) { cout << (*ite).m_nVertexIndexA << "->" << (*ite).m_nVertexIndexB << " : " << (*ite).m_nWeight << endl ; } } private: vector<vector<int> > m_nvGraph ; // 无向连通图 vector<TreeNode> m_tnMSTree ; // 最小生成树 int m_nNodeCount ; } ; int main() { const int cnNodeCount = 6 ; vector<vector<int> > graph (cnNodeCount) ; for (size_t i = 0; i < graph.size() ; ++ i) { graph[i].resize (cnNodeCount, numeric_limits<int>::max()) ; } graph[0][1]= 6 ; graph[0][2] = 1 ; graph[0][3] = 5 ; graph[1][2] = 5 ; graph[1][4] = 3 ; graph[2][3] = 5 ; graph[2][4] = 6 ; graph[2][5] = 4 ; graph[3][5] = 2 ; graph[4][5] = 6 ; graph[1][0]= 6 ; graph[2][0] = 1 ; graph[3][0] = 5 ; graph[2][1] = 5 ; graph[4][1] = 3 ; graph[3][2] = 5 ; graph[4][2] = 6 ; graph[5][2] = 4 ; graph[5][3] = 2 ; graph[5][4] = 6 ; MST_Prim mstp (graph) ; mstp.DoPrim () ; return 0 ; } ## 三、Kruskal算法 ## 当图的边数为e时,Kruskal算法所需的时间是O(eloge)。当e = Ω(n^2)时,Kruskal算法比Prim算法差;但当e = o(n^2)时,Kruskal算法比Prim算法好得多。 给定无向连同带权图G = (V,E),V = \{1,2,...,n\}。Kruskal算法构造G的最小生成树的基本思想是: (1)首先将G的n个顶点看成n个孤立的连通分支。将所有的边按权从小大排序。 (2)从第一条边开始,依边权递增的顺序检查每一条边。并按照下述方法连接两个不同的连通分支:当查看到第k条边(v,w)时,如果端点v和w分别是当前两个不同的连通分支T1和T2的端点是,就用边(v,w)将T1和T2连接成一个连通分支,然后继续查看第k+1条边;如果端点v和w在当前的同一个连通分支中,就直接再查看k+1条边。这个过程一个进行到只剩下一个连通分支时为止。 此时,已构成G的一棵最小生成树。 ![2010120212285861.jpg][] Kruskal算法的选边过程: 1 -> 3 : 1 4 -> 6 : 2 2 -> 5 : 3 3 -> 4 : 4 2 -> 3 : 5 /* 主题:贪心算法之最小生成树(Kruskal算法) * 作者:chinazhangjie * 邮箱:chinajiezhang@gmail.com * 开发语言:C++ * 开发环境:Visual Studio 2005 * 时间:2010.12.01 */ #include <iostream> #include <vector> #include <queue> #include <limits> using namespace std ; struct TreeNode { public: TreeNode (int nVertexIndexA = 0, int nVertexIndexB = 0, int nWeight = 0) : m_nVertexIndexA (nVertexIndexA), m_nVertexIndexB (nVertexIndexB), m_nWeight (nWeight) { } friend bool operator < (const TreeNode& lth, const TreeNode& rth) { return lth.m_nWeight > rth.m_nWeight ; } public: int m_nVertexIndexA ; int m_nVertexIndexB ; int m_nWeight ; } ; // 并查集 class UnionSet { public: UnionSet (int nSetEleCount) : m_nSetEleCount (nSetEleCount) { __init() ; } // 合并i,j。如果i,j同在集合中,返回false。否则返回true bool Union (int i, int j) { int ifather = __find (i) ; int jfather = __find (j) ; if (ifather == jfather ) { return false ; // copy (m_nvFather.begin(), m_nvFather.end(), ostream_iterator<int> (cout, " ")); // cout << endl ; } else { m_nvFather[jfather] = ifather ; // copy (m_nvFather.begin(), m_nvFather.end(), ostream_iterator<int> (cout, " ")); // cout << endl ; return true ; } } private: // 初始化并查集 int __init() { m_nvFather.resize (m_nSetEleCount) ; for (vector<int>::size_type i = 0 ; i < m_nSetEleCount; ++ i ) { m_nvFather[i] = static_cast<int>(i) ; // cout << m_nvFather[i] << " " ; } // cout << endl ; return 0 ; } // 查找index元素的父亲节点 并且压缩路径长度 int __find (int nIndex) { if (nIndex == m_nvFather[nIndex]) { return nIndex; } return m_nvFather[nIndex] = __find (m_nvFather[nIndex]); } private: vector<int> m_nvFather ; // 父亲数组 vector<int>::size_type m_nSetEleCount ; // 集合中结点个数 } ; class MST_Kruskal { typedef priority_queue<TreeNode> MinHeap ; public: MST_Kruskal (const vector<vector<int> >& graph) { m_nNodeCount = static_cast<int>(graph.size ()) ; __getMinHeap (graph) ; } void DoKruskal () { UnionSet us (m_nNodeCount) ; int k = 0 ; while (m_minheap.size() != 0 && k < m_nNodeCount - 1) { TreeNode tn = m_minheap.top () ; m_minheap.pop () ; // 判断合理性 if (us.Union (tn.m_nVertexIndexA, tn.m_nVertexIndexB)) { m_tnMSTree.push_back (tn) ; ++ k ; } } // 输出结果 for (size_t i = 0; i < m_tnMSTree.size() ; ++ i) { cout << m_tnMSTree[i].m_nVertexIndexA << "->" << m_tnMSTree[i].m_nVertexIndexB << " : " << m_tnMSTree[i].m_nWeight << endl ; } } private: void __getMinHeap (const vector<vector<int> >& graph) { for (int i = 0; i < m_nNodeCount; ++ i) { for (int j = 0; j < m_nNodeCount; ++ j) { if (graph[i][j] != numeric_limits<int>::max()) { m_minheap.push (TreeNode(i, j, graph[i][j])) ; } } } } private: vector<TreeNode> m_tnMSTree ; int m_nNodeCount ; MinHeap m_minheap ; } ; int main () { const int cnNodeCount = 6 ; vector<vector<int> > graph (cnNodeCount) ; for (size_t i = 0; i < graph.size() ; ++ i) { graph[i].resize (cnNodeCount, numeric_limits<int>::max()) ; } graph[0][1]= 6 ; graph[0][2] = 1 ; graph[0][3] = 3 ; graph[1][2] = 5 ; graph[1][4] = 3 ; graph[2][3] = 5 ; graph[2][4] = 6 ; graph[2][5] = 4 ; graph[3][5] = 2 ; graph[4][5] = 6 ; graph[1][0]= 6 ; graph[2][0] = 1 ; graph[3][0] = 3 ; graph[2][1] = 5 ; graph[4][1] = 3 ; graph[3][2] = 5 ; graph[4][2] = 6 ; graph[5][2] = 4 ; graph[5][3] = 2 ; graph[5][4] = 6 ; MST_Kruskal mst (graph); mst.DoKruskal () ; } [2010120212285861.jpg]: /images/20220524/9e2dd2e456da4cddbc6c8c2474db549d.png [http_sjjp.tjuci.edu.cn_sjjg_DataStructure_DS_web_flashhtml_prim.htm]: http://sjjp.tjuci.edu.cn/sjjg/DataStructure/DS/web/flashhtml/prim.htm
相关 最小生成树 最小生成树定义: 在一给定的无向图 G = (V, E) 中,(u, v) 代表连接顶点 u 与顶点 v 的边(即![(u, v)\\in E][u_ v_in E]),而 末蓝、/ 2022年09月20日 15:18/ 0 赞/ 204 阅读
相关 生成树相关问题(最小生成树变形,次小生成树,最小度限度生成树,极差最小生成树) 生成树相关问题(最小生成树变形,次小生成树,最小度限度生成树,极差最小生成树) 视频:[https://www.bilibili.com/video/BV1G64y187ke Bertha 。/ 2022年09月12日 05:52/ 0 赞/ 215 阅读
相关 最小生成树 一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。 最小生成树在n个顶点的情形下,有n-1条边。生成树是对连 忘是亡心i/ 2022年08月03日 05:28/ 0 赞/ 225 阅读
相关 最小生成树 邻接矩阵建图+prim朴素[算法][Link 1] 代码通过HDU1102 \[cpp\] [view plain][] [copy][view plain] 1. 超、凢脫俗/ 2022年06月09日 04:27/ 0 赞/ 82 阅读
相关 最小生成树 有n个城市,m条道路,现在输入n到m的道路的长度,用最少的边将图连接,输出让图连接的最小值 这道题我研究了好长时间才把答案看明白,现在给大家分享一下 具体代码如下 爱被打了一巴掌/ 2022年05月30日 08:56/ 0 赞/ 183 阅读
相关 最小生成树 设G = (V,E)是无向连通带权图,即一个网络。E中的每一条边(v,w)的权为c\[v\]\[w\]。如果G的子图G’是一棵包含G的所有顶点的树,则称G’为G的生成树。生成 红太狼/ 2022年05月24日 11:41/ 0 赞/ 272 阅读
相关 最小生成树 问题提出: 要在n个城市间建立通信联络网。顶点:表示城市,权:城市间通信线路的花费代价。希望此通信网花费代价最小。 问题分析: 答案只能从生成树中找,因为要做到任何 雨点打透心脏的1/2处/ 2022年03月18日 12:28/ 0 赞/ 298 阅读
相关 最小生成树 kruskal算法基本思路:先对边按权重从小到大排序,先选取权重最小的一条边,如果该边的两个节点均为不同的分量,则加入到最小生成树,否则计算下一条边,直到遍历完所有的边。 p ゝ一纸荒年。/ 2022年02月25日 14:20/ 0 赞/ 274 阅读
相关 最小生成树 最小生成树 什么是最小生成树 给定一张无向带权图G=(V,E,W),从中挑选出|V|-1条边,使得V中任意两点都有直接或者间接的路径 显然,最后得到的子 浅浅的花香味﹌/ 2021年12月22日 01:57/ 0 赞/ 329 阅读
相关 最小生成树 Jungle Roads [http://poj.org/problem?id=1251][http_poj.org_problem_id_1251] Descrip 逃离我推掉我的手/ 2021年11月29日 22:28/ 0 赞/ 434 阅读
还没有评论,来说两句吧...