基于Boost的带权图最小生成树深度优先遍历自定义访问器问题
解决Boost Graph自定义深度优先访问器的问题
嘿,我懂你现在卡在自定义DFS访问器这儿的苦恼!结合你提到的已经完成图和最小生成树构建的背景,我给你整理了一套完整的解决方案,包括自定义访问器的正确写法,以及如何在最小生成树上执行DFS的步骤。
核心思路:自定义DFS访问器的正确姿势
Boost的depth_first_search需要一个符合Visitor Concept的对象,最简单的方式是继承boost::default_dfs_visitor,然后重写你需要的回调方法(比如发现顶点、完成顶点、遍历树边等),不用实现所有方法——默认访问器会提供空实现。
完整代码示例
下面是结合你已有逻辑的完整代码,包含图构建、Kruskal最小生成树生成、自定义DFS访问器实现和遍历:
#include <iostream> #include <boost/graph/adjacency_list.hpp> #include <boost/graph/kruskal_min_spanning_tree.hpp> #include <boost/graph/depth_first_search.hpp> #include <boost/graph/graph_traits.hpp> #include <vector> #include <string> // 定义顶点和边的属性结构 struct VertexProperty { std::string name; // 顶点可以带名称属性 }; struct EdgeProperty { int weight; // 边的权重属性 }; // 定义图类型:无向图,顶点/边用向量存储,带自定义属性 using Graph = boost::adjacency_list< boost::vecS, boost::vecS, boost::undirectedS, VertexProperty, EdgeProperty >; using Vertex = boost::graph_traits<Graph>::vertex_descriptor; using Edge = boost::graph_traits<Graph>::edge_descriptor; // 自定义DFS访问器:继承默认访问器,重写需要的回调 struct CustomDFSVisitor : public boost::default_dfs_visitor { // 第一次访问到顶点时触发 template <typename Vertex, typename Graph> void discover_vertex(Vertex v, const Graph& g) const { std::cout << "🔍 发现顶点:" << g[v].name << std::endl; } // 完成该顶点所有邻接顶点的访问后触发 template <typename Vertex, typename Graph> void finish_vertex(Vertex v, const Graph& g) const { std::cout << "✅ 完成顶点:" << g[v].name << std::endl; } // 遍历生成树的树边时触发 template <typename Edge, typename Graph> void tree_edge(Edge e, const Graph& g) const { auto src = source(e, g); auto tgt = target(e, g); std::cout << "🔗 遍历树边:" << g[src].name << " -> " << g[tgt].name << " | 权重:" << g[e].weight << std::endl; } }; int main() { // 1. 构建原始带权图 Graph g; Vertex v0 = add_vertex(VertexProperty{"A"}, g); Vertex v1 = add_vertex(VertexProperty{"B"}, g); Vertex v2 = add_vertex(VertexProperty{"C"}, g); Vertex v3 = add_vertex(VertexProperty{"D"}, g); // 添加带权边 add_edge(v0, v1, EdgeProperty{2}, g); add_edge(v0, v2, EdgeProperty{3}, g); add_edge(v1, v2, EdgeProperty{1}, g); add_edge(v1, v3, EdgeProperty{4}, g); add_edge(v2, v3, EdgeProperty{5}, g); // 2. 用Kruskal算法生成最小生成树 std::vector<Edge> mst_edges; boost::kruskal_minimum_spanning_tree(g, std::back_inserter(mst_edges)); std::cout << "=== 最小生成树的边 ===" << std::endl; for (const Edge& e : mst_edges) { std::cout << g[source(e, g)].name << " - " << g[target(e, g)].name << " | 权重:" << g[e].weight << std::endl; } // 3. 构建最小生成树的独立图(方便DFS遍历,避免遍历原始图的非树边) Graph mst_graph; std::vector<Vertex> mst_vertex_map(num_vertices(g)); // 复制原始图的顶点属性到生成树图 for (Vertex v : boost::make_iterator_range(vertices(g))) { mst_vertex_map[v] = add_vertex(g[v], mst_graph); } // 添加生成树的边 for (const Edge& e : mst_edges) { add_edge(mst_vertex_map[source(e, g)], mst_vertex_map[target(e, g)], g[e], mst_graph); } // 4. 执行自定义DFS遍历 std::cout << "\n=== 深度优先遍历最小生成树 ===" << std::endl; CustomDFSVisitor visitor; // 必须提供颜色映射:跟踪顶点的访问状态(未访问/正在访问/已访问) std::vector<boost::default_color_type> vertex_colors(num_vertices(mst_graph)); boost::depth_first_search( mst_graph, boost::visitor(visitor) .color_map(&vertex_colors[0]) // 颜色映射是DFS的必填参数 ); return 0; }
你可能踩坑的几个关键点
- 忘记继承
boost::default_dfs_visitor:如果不继承,你需要实现所有Visitor Concept要求的方法,很容易出错。继承默认访问器只需要重写你关心的回调。 - 缺少颜色映射(color_map):DFS必须跟踪顶点的访问状态,否则会陷入循环或者无法正确遍历。一定要提供
color_map参数。 - 回调函数签名错误:每个回调都是模板函数,参数必须包含顶点/边描述符和const引用的图,否则Boost的DFS算法无法正确调用。
- 遍历原始图而非生成树:如果你直接在原始图上执行DFS,会遍历所有边,包括非生成树的边。所以单独构建生成树的图,或者用
edge_filter来过滤出树边,都是可行的方案,上面的示例用了更直观的独立图方式。
内容的提问来源于stack exchange,提问作者Maecky
相关产品推荐
相关产品推荐

