C++ Boost Graph库多目标Dijkstra最短路径实现技术疑问
基于Boost Graph处理超大规模有向多图的问题解答
1. OutEdgeList与VertexList容器类型选择
针对22亿边、2.5亿顶点的规模,兼顾图构建与Dijkstra效率,容器类型需结合顶点ID特性选择:
- 若顶点ID为连续整数(如0到2.499亿):
VertexList选vecS——基于连续vector存储,顶点ID直接对应数组索引,构建时可通过resize()一次性分配内存,访问与查找效率拉满;OutEdgeList选vecS——出边以连续数组存储,Dijkstra遍历出边时缓存友好,批量插入边的效率也远高于链表类容器。
对应的图类型定义:using Graph = boost::adjacency_list< boost::vecS, // OutEdgeList boost::vecS, // VertexList boost::directedS, // 有向图 boost::no_property,// 顶点无附加属性 boost::property<boost::edge_weight_t, uint64_t> // 边权重属性 >; - 若顶点ID不连续或范围极大:
VertexList选hashS——基于哈希表存储顶点,自动处理非连续ID,避免vecS带来的内存浪费;OutEdgeList仍选vecS,保证出边遍历的缓存效率。
2. 图构建方式效率对比与数值型ID适配
常见两种构建方式的效率差异与适配方案:
- 方式1:先批量创建顶点,再批量添加边:
仅适用于vecS作为VertexList的场景,需提前知晓顶点ID范围,通过graph.resize(max_vertex_id + 1)一次性分配顶点内存,再用boost::add_edge_range批量导入边数据。这种方式无顶点存在性检查,构建速度最快。 - 方式2:直接添加边,自动创建不存在的顶点:
适用于hashS作为VertexList的场景,无需提前处理顶点,调用add_edge(from[i], to[i], weights[i], graph)时会自动创建不存在的顶点。对于非连续数值型ID,这是更灵活的选择。
优化建议:优先用boost::add_edge_range替代循环调用add_edge,减少函数调用开销,批量导入22亿边时能显著提升效率。适配数值型ID的图类型:using Graph = boost::adjacency_list< boost::vecS, boost::hashS, boost::directedS, boost::no_property, boost::property<boost::edge_weight_t, uint64_t> >;
3. Dijkstra算法三种输出的实现
基于boost::dijkstra_shortest_paths,通过指定不同属性映射实现三种输出:
仅输出路径距离
只需准备距离容器,初始化后传入distance_map参数:
// 假设source是指定源点,graph已构建完成 size_t num_v = boost::num_vertices(graph); std::vector<uint64_t> distances(num_v, std::numeric_limits<uint64_t>::max()); distances[source] = 0; boost::dijkstra_shortest_paths( graph, source, boost::distance_map(boost::make_iterator_property_map( distances.begin(), boost::get(boost::vertex_index, graph) )) ); // 访问目标点距离:distances[target_id]
仅输出路径顶点ID
需准备前驱顶点容器,传入predecessor_map参数,之后通过回溯前驱获取路径:
size_t num_v = boost::num_vertices(graph); std::vector<Graph::vertex_descriptor> predecessors(num_v, boost::graph_traits<Graph>::null_vertex()); boost::dijkstra_shortest_paths( graph, source, boost::predecessor_map(boost::make_iterator_property_map( predecessors.begin(), boost::get(boost::vertex_index, graph) )) ); // 从目标点回溯到源点,生成路径 std::vector<Graph::vertex_descriptor> path; for (auto v = target_id; v != boost::graph_traits<Graph>::null_vertex(); v = predecessors[v]) { path.push_back(v); } std::reverse(path.begin(), path.end()); // 反转后得到从源到目标的顺序
同时输出距离与路径顶点ID
同时传入distance_map和predecessor_map参数,结合上述两种逻辑即可:
size_t num_v = boost::num_vertices(graph); std::vector<uint64_t> distances(num_v, std::numeric_limits<uint64_t>::max()); std::vector<Graph::vertex_descriptor> predecessors(num_v, boost::graph_traits<Graph>::null_vertex()); distances[source] = 0; boost::dijkstra_shortest_paths( graph, source, boost::distance_map(boost::make_iterator_property_map( distances.begin(), boost::get(boost::vertex_index, graph) )).predecessor_map(boost::make_iterator_property_map( predecessors.begin(), boost::get(boost::vertex_index, graph) )) ); // 后续可同时访问distances[target_id]和回溯路径
内容的提问来源于stack exchange,提问作者user
相关产品推荐
相关产品推荐

