【图】图的遍历以及最小生成树
来源:互联网 发布:js控制div旋转 编辑:程序博客网 时间:2024/06/05 10:12
图的遍历
图的遍历图和树的遍历类似,那就是从图中某一顶点出发访遍图中其余顶点,且使每一个顶点仅被访问一次,这个过程就叫做图的遍历。
对于图的遍历来说,如何避免因回路陷入死循环,就需要科学地设计遍历方案,通过有两种遍历次序方案:深度优先遍历和广度优先遍历。
1、深度优先遍历 DFS
深度优先遍历(Depth_First_Search),也称为深度优先搜索,简称DFS。
沿着树的深度遍历树的节点,尽可能深的搜索树的分支节点,当节点v的所有边被访问过,搜索将回溯到发现节点v的那条边的起始位置,然后继续访问未被访问过的节点,直到所有的节点被访问。
此就需要标记访问过的节点。
类似于二叉树的前序遍历。
代码时基于邻接表实现的。
对于图来说,不一定都是连通图,所以深度优先遍历完其中的连通图后,还要查看还有节点是非连通图未访问的。
//深度优先遍历: void DFS(const V& v) { cout << "深度优先遍历:" << endl; //标记访问过的节点 vector<bool> visited(_vex.size(), false); int idx = GetIndexOfVertex(v); _DFS(idx, visited); //非连通的访问 for (size_t i = 0; i < _vex.size(); ++i) { if (!visited[i]) _DFS(i, visited); } cout << "NULL" << endl; } void _DFS(size_t idx,vector<bool>& visitted) { //未被访问的节点,做广度搜索 if(!visited[idx]) { cout << idx << "-->"; visited[idx] = true; //邻接表,所以访问单链表 LinkEdge<W>* pCur = _LinkTable[idx]; while (pCur) { _DFS(pCur->_desIndex, visited); pCur = pCur->_pNext; } } }
2、广度优先遍历 BFS
BFS是从根节点开始出发,沿着树的宽度遍历树的节点,直到所有的节点被访问。
有点类似于树的层次遍历。
下面代码时基于邻接表实现的
//广度优先遍历图:队列 void BFS(const V& v) { cout << "广度优先遍历:" << endl; vector<bool> visited(_vex.size(), false); _BFS(v, visited); //非连通的访问 for (size_t i = 0; i < _vex.size(); ++i) { if (!visited[i]) _BFS(_vex[i],visited); } cout << "NULL" << endl; } void _BFS(const V& v,vector<bool>& visited) { queue<size_t> q; int idx = GetIndexOfVertex(v); q.push(idx); while(!q.empty()) { if (!visited[idx]) { cout << idx << "-->"; visited[idx] = true; q.pop(); LinkEdge<W>* pCur = _LinkTable[idx]; while (pCur) { q.push(pCur->_desIndex); pCur = pCur->_pNext; } } } }
图的最小生成树
生成树是将图中所有顶点以最少的边连通的子图。
权值和最小的生成树就是最小生成树。
从最小生成树的定义可知,构造有n个结点的无向连通带权图的最小生成树,必须满足以下三条:
(1)构造的最小生成树必须包括n个结点;
(2)构造的最小生成树中有且只有n-1条边;
(3)构造的最小生成树中不存在回路。
构造最小生成树的方法有许多种,典型的构造方法有两种,一种称作普里姆(Prim)算法,另一种称作克鲁斯卡尔(Kruskal)算法。
普里姆算法
普里姆算法思想是:利用贪心的算法。
从单一顶点开始,普里姆算法按照以下步骤逐步扩大树中所含顶点的数目,直到遍及连通图的所有顶点。
(1)输入:一个加权连通图,其中顶点集合为V,边集合为E;
(2)初始化:Vnew = {x},其中x为集合V中的任一节点(起始点),Enew = {};
(3)重复下列操作,直到Vnew = V:
在集合E中选取权值最小的边(u, v),其中u为集合Vnew中的元素,而v则不是(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一);
将v加入集合Vnew中,将(u, v)加入集合Enew中;
(4)输出:使用集合Vnew和Enew来描述所得到的最小生成树。
基于邻接表代码实现
在寻找最小值,可以借助堆,
//比较器struct Biger { bool operator()(LinkEdge<W>* Left, LinkEdge<W>* Right) { return Left->_edge > Right->_edge; } };//最小生成树prime算法 pair<GraphLink<V, W>, bool> Prime(const V& vertex) { //定义一个图来接收最小生成树,下面初始化图g GraphLink<V, W> g; g._vex.resize(_vex.size()); for (size_t idx = 0; idx < _vex.size(); ++idx) g._vex[idx] = _vex[idx]; g._IsDirected = false; g._LinkTable.resize(_vex.size()-1, NULL); //给一个起始顶点,将顶点为起点的边放到最小堆中,每次从最小堆中取堆顶加到图中(判断是否已经加入) vector<bool> flag(_vex.size(), false);//标记顶点是否已经加入图中 vector<LinkEdge<W> *> vEdge; int index = GetIndexOfVertex(vertex); LinkEdge<W> *pCur = _LinkTable[index]; while (pCur) { vEdge.push_back(pCur); pCur = pCur->_pNext; } //构建堆 make_heap(vEdge.begin(), vEdge.end(), Biger()); int count = 0;//统计加入边的数量,n-1条边就是最小生成树 while (true) { //从最小堆中取出 pCur = vEdge[0]; pop_heap(vEdge.begin(), vEdge.end(), Biger()); vEdge.pop_back(); if (!flag[pCur->_desIndex]) { g._AddEdge(pCur->_srcIndex, pCur->_desIndex, pCur->_edge); if (++count == _vex.size() - 1) return make_pair(g, true); flag[pCur->_srcIndex] = true; } index = pCur->_desIndex; if (!flag[index]) { pCur = _LinkTable[index]; while (pCur) { if (!flag[pCur->_desIndex]) { vEdge.push_back(pCur); push_heap(vEdge.begin(), vEdge.end(), Biger()); } pCur = pCur->_pNext; } } } return make_pair(g, false); }
2、克鲁斯卡尔算法
其思想就是直接了当的贪心,每次都将权值最短的边收进来:可以将每个顶点都看成一棵树,然后将权值最短的边的顶点连接起来,在不构成回路的情况下,将这些森林合并成一棵树。
考虑到构成回路的问题,这里我用到并查集。
并查集的实现 可以看下一篇博客,)
并查集的实现
//排序比较器 struct Compare { bool operator()(LinkEdge<W>* Left, LinkEdge<W>* Right) { return Left->_edge < Right->_edge; } }; //无向图的最小生成树 pair<GraphLink<V, W>, bool> GetMinTree() { //1 将所有的边放在vector中,过滤掉重复的边 vector<LinkEdge<W> *> edge; for (size_t i = 0; i < _LinkTable.size(); ++i) { LinkEdge<W> * pCur = _LinkTable[i]; while (pCur) { if (pCur->_srcIndex < pCur->_desIndex)//过滤掉重复的边 edge.push_back(pCur); pCur = pCur->_pNext; } } //2 将vector 排序 sort(edge.begin(), edge.end(), Compare()); //生成图,保存结果 GraphLink<V, W> g; g._vex.resize(_vex.size()); for (size_t idx = 0; idx < _vex.size(); ++idx) g._vex[idx] = _vex[idx]; g._LinkTable.resize(_LinkTable.size(), NULL); g._IsDirected = false; //3 从vector中取出最小值,添加到图中 //并查集初始化 UnionFindSet un(_vex.size()); int count = 0;//统计加入的边数量 for (size_t j = 0; j < edge.size(); ++j) { LinkEdge<W>* pEdge = edge[j]; //检测是否构成了环 if (!(un.IsSameSet(pEdge->_srcIndex,pEdge->_desIndex))) { g._AddEdge(pEdge->_srcIndex,pEdge->_desIndex, pEdge->_edge); un.Union(pEdge->_srcIndex, pEdge->_desIndex); if (++count == _vex.size() - 1) { g._LinkTable.resize(count); return make_pair(g, true); } } } return make_pair(g, false); }
- 【图】图的遍历以及最小生成树
- 图的遍历、最小生成树以及单源最短路径
- 最小生成树和图的遍历
- 图的遍历与最小生成树
- 图的存储、遍历和求最小生成树
- 图的遍历、最小生成树、最短路径
- 图的邻接表(广度优先遍历,深度优先遍历,最小生成树(Kruskal算法))
- 图的广度遍历、深度遍历及最小生成树书算法(Prim、Kruskal)
- 图的遍历和生成树求解实现|图遍历,生成树,实现,邻接矩阵,邻接表,深度广度遍历,最小生成树
- 邻接矩阵实现图+深度/广度优先遍历+最小生成树
- 邻接矩阵实现图+深度/广度优先遍历+最小生成树
- 图基本算法:深度广度遍历最小生成树
- 数据结构:图的存储、图的遍历、最小生成树、最短路径、拓扑排序
- 图的遍历及最小生成树(prim,kruskal)的实现
- 图的操作(增删改查、遍历、最小生成树)的实现
- [数据结构]--图(图的遍历,最小生成树,最短路径算法)
- 数据结构:图——图的遍历、最小生成树、最短路径算法
- [数据结构]第六次作业:图的建立、遍历、最小生成树、最短路径
- java.util.logging.logger的使用
- C++Builder PID控制一阶惯性系统
- UVA
- link和@import的区别
- 算法导论 最小生成树MST-KRUSKAL
- 【图】图的遍历以及最小生成树
- Spring顾问
- 霍夫丁不等式及其他相关不等式证明
- MaterialDesign(三)
- PHP分页原理
- Surround360 Render目录下RENDER文档——中文翻译
- 自省
- 快速排序和随机快速排序
- HDU1092-A+B for Input-Output Practice (IV)