【发布时间】:2012-02-07 09:46:24
【问题描述】:
我对如何使用 boost 库实际创建 Graph 感到困惑,我查看了示例代码,但没有 cmets 解释它的作用。
您如何制作图形,并在执行过程中添加顶点和边?
【问题讨论】:
-
stackoverflow.com/questions/3100146/… 的可能副本有一个很好的答案(包括自定义顶点、边类型的操作方法)
我对如何使用 boost 库实际创建 Graph 感到困惑,我查看了示例代码,但没有 cmets 解释它的作用。
您如何制作图形,并在执行过程中添加顶点和边?
【问题讨论】:
这是一个简单的例子,使用邻接表并执行拓扑排序:
#include <iostream>
#include <deque>
#include <iterator>
#include "boost/graph/adjacency_list.hpp"
#include "boost/graph/topological_sort.hpp"
int main()
{
// Create a n adjacency list, add some vertices.
boost::adjacency_list<> g(num tasks);
boost::add_vertex(0, g);
boost::add_vertex(1, g);
boost::add_vertex(2, g);
boost::add_vertex(3, g);
boost::add_vertex(4, g);
boost::add_vertex(5, g);
boost::add_vertex(6, g);
// Add edges between vertices.
boost::add_edge(0, 3, g);
boost::add_edge(1, 3, g);
boost::add_edge(1, 4, g);
boost::add_edge(2, 1, g);
boost::add_edge(3, 5, g);
boost::add_edge(4, 6, g);
boost::add_edge(5, 6, g);
// Perform a topological sort.
std::deque<int> topo_order;
boost::topological_sort(g, std::front_inserter(topo_order));
// Print the results.
for(std::deque<int>::const_iterator i = topo_order.begin();
i != topo_order.end();
++i)
{
cout << tasks[v] << endl;
}
return 0;
}
我同意 boost::graph 文档可能令人生畏,但值得拥有 look。
我不记得印刷书的内容是否相同,我怀疑它在眼睛上更容易一些。我实际上是从书中学会了使用 boost:graph 的。不过,学习曲线可能会感觉非常陡峭。我参考的书和评论可以在here找到。
【讨论】:
num tasks 位肯定不会工作。
这是基于 boost::graph 网站上给出的示例,添加了 cmets:
#include <iostream>
#include <utility>
#include <algorithm>
#include <vector>
#include "boost/graph/graph_traits.hpp"
#include "boost/graph/adjacency_list.hpp"
using namespace boost;
int main(int argc, char *argv[])
{
//create an -undirected- graph type, using vectors as the underlying containers
//and an adjacency_list as the basic representation
typedef adjacency_list<vecS, vecS, undirectedS> UndirectedGraph;
//Our set of edges, which basically are just converted into ints (0-4)
enum {A, B, C, D, E, N};
const char *name = "ABCDE";
//An edge is just a connection between two vertitices. Our verticies above
//are an enum, and are just used as integers, so our edges just become
//a std::pair<int, int>
typedef std::pair<int, int> Edge;
//Example uses an array, but we can easily use another container type
//to hold our edges.
std::vector<Edge> edgeVec;
edgeVec.push_back(Edge(A,B));
edgeVec.push_back(Edge(A,D));
edgeVec.push_back(Edge(C,A));
edgeVec.push_back(Edge(D,C));
edgeVec.push_back(Edge(C,E));
edgeVec.push_back(Edge(B,D));
edgeVec.push_back(Edge(D,E));
//Now we can initialize our graph using iterators from our above vector
UndirectedGraph g(edgeVec.begin(), edgeVec.end(), N);
std::cout << num_edges(g) << "\n";
//Ok, we want to see that all our edges are now contained in the graph
typedef graph_traits<UndirectedGraph>::edge_iterator edge_iterator;
//Tried to make this section more clear, instead of using tie, keeping all
//the original types so it's more clear what is going on
std::pair<edge_iterator, edge_iterator> ei = edges(g);
for(edge_iterator edge_iter = ei.first; edge_iter != ei.second; ++edge_iter) {
std::cout << "(" << source(*edge_iter, g) << ", " << target(*edge_iter, g) << ")\n";
}
std::cout << "\n";
//Want to add another edge between (A,E)?
add_edge(A, E, g);
//Print out the edge list again to see that it has been added
for(edge_iterator edge_iter = ei.first; edge_iter != ei.second; ++edge_iter) {
std::cout << "(" << source(*edge_iter, g) << ", " << target(*edge_iter, g) << ")\n";
}
//Finally lets add a new vertex - remember the verticies are just of type int
int F = add_vertex(g);
std::cout << F << "\n";
//Connect our new vertex with an edge to A...
add_edge(A, F, g);
//...and print out our edge set once more to see that it was added
for(edge_iterator edge_iter = ei.first; edge_iter != ei.second; ++edge_iter) {
std::cout << "(" << source(*edge_iter, g) << ", " << target(*edge_iter, g) << ")\n";
}
return 0;
}
【讨论】:
我认为您会发现以下资源非常有用。
如果你不熟悉图论或需要复习,那么看看 boost 的 Review of Elementary Graph Theory: http://www.boost.org/doc/libs/1_58_0/libs/graph/doc/graph_theory_review.html
这本入门读物有助于理解术语、数据结构如何表示图(邻接矩阵、邻接列表等)和算法(广度优先搜索、深度优先搜索、最短路径等)。
有关详细描述的用于创建图形的示例代码,请查看 Boris Schäling 在线书籍的以下部分 - Boost C++ 库: http://theboostcpplibraries.com/boost.graph-vertices-and-edges
Boris 解释了如何使用相邻列表来处理顶点和边。对代码进行了详尽的解释,因此您可以理解每个示例。
了解 adjacency_list 的模板参数很重要。例如,您想要有向图还是无向图?您是否希望您的图包含具有相同末端节点的多个边(即多重图)?性能也发挥作用。 Boris 的书解释了其中一些,但您可以在此处找到有关使用相邻列表的更多信息: http://www.boost.org/doc/libs/1_58_0/libs/graph/doc/using_adjacency_list.html
如果您想为顶点、边甚至图形本身使用自定义对象,那么您将需要使用捆绑属性。以下链接将有助于使用捆绑属性: http://www.boost.org/doc/libs/1_58_0/libs/graph/doc/bundles.html
也许这也是一个例子: adding custom vertices to a boost graph
有多种方法可以检测循环依赖,包括:
深度优先搜索: 一种简单的方法是执行深度优先搜索并检测搜索是否进入当前搜索树中已发现的顶点。下面是一个使用 boost 的深度优先搜索来检测循环依赖的例子: http://www.boost.org/doc/libs/1_58_0/libs/graph/doc/file_dependency_example.html#sec:cycles
拓扑排序: 也可以使用topological sort 检测周期。 boost 提供了一个 topological_sort 算法: http://www.boost.org/doc/libs/1_58_0/libs/graph/doc/topological_sort.html
拓扑排序适用于有向无环图 (DAG)。如果传入循环图,则抛出异常,从而表明该图具有循环依赖关系。 topological_sort 包括深度优先搜索,但也提供顶点的线性排序。这是一个例子: http://www.boost.org/doc/libs/1_58_0/libs/graph/doc/file_dependency_example.html#sec:cycles
强连接组件: 此外,找到强连通分量可以指示图是否具有循环: http://www.personal.kent.edu/~rmuhamma/Algorithms/MyAlgorithms/GraphAlgor/strongComponent.htm
boost 的 strong_components 函数使用 Tarjan 算法计算有向图的强连通分量。 http://www.boost.org/doc/libs/1_58_0/libs/graph/doc/strong_components.html
另一个有用的链接是已经提供的——boost 的 文件依赖示例,它展示了如何设置源代码文件图,根据它们的编译顺序(拓扑排序)对它们进行排序,确定什么文件可以同时编译,并确定循环依赖: http://www.boost.org/doc/libs/1_58_0/libs/graph/doc/file_dependency_example.html
【讨论】:
Boost 的adjacency_list 是一个不错的方法,这个例子创建了一个有向图并使用 AT&T 的 GraphViz 输出了该图的图像:
#include <iostream>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphviz.hpp>
int main()
{
using namespace std;
using namespace boost;
/* define the graph type
listS: selects the STL list container to store
the OutEdge list
vecS: selects the STL vector container to store
the vertices
directedS: selects directed edges
*/
typedef adjacency_list< listS, vecS, directedS > digraph;
// instantiate a digraph object with 8 vertices
digraph g(8);
// add some edges
add_edge(0, 1, g);
add_edge(1, 5, g);
add_edge(5, 6, g);
add_edge(2, 3, g);
add_edge(2, 4, g);
add_edge(3, 5, g);
add_edge(4, 5, g);
add_edge(5, 7, g);
// represent graph in DOT format and send to cout
write_graphviz(cout, g);
return 0;
}
输出是一个 DOT 文件,您可以将其快速输入到 GraphViz 附带的dot 实用程序中。
【讨论】:
可以在此处找到一些简短而中肯的 Boost C++ 库入门指南:
此处列出的这些代码示例似乎是最新的,并且可以正常编译和工作。我发现一些关于使用 Boost Graph Library 的在线文档似乎已经过时或产生编译错误。
这里有许多工作示例,包括创建有向图和无向图、打印边的权重、使用 Kruskal 算法查找最小生成树以及最大流问题。
【讨论】: