boost::edge_reverse_t
一、boost::edge_reverse_t是什么
1 定义
boost::edge_reverse_t是一个Edge Property Tag,用于标记一条边的“反向边(reverse edge)”。
重点:
- 它本身不存储数据
- 只是一个类型标签
- 用于 property_map 绑定 edge_descriptor
1.2 数学语义(最大流背景)
对于一条边 (e=(u→v)e = (u \to v)e=(u→v)):
- 反向边 (erev=(v→u)e^{rev} = (v \to u)erev=(v→u))
- 残量更新公式:
r(e)=c(e)−f(e),r(erev)=f(e) r(e) = c(e) - f(e), \quad r(e^{rev}) = f(e)r(e)=c(e)−f(e),r(erev)=f(e)
算法必须能通过 edge_reverse 找到反向边。
二、edge_reverse_t在 Boost.Graph 类型系统中的角色
structedge_reverse_t{};- 属于property tag
- 内置成员类型:
kind = edge_property_tag - 作为编译期契约让算法可以安全访问 reverse edge
2.1 与edge_capacity_t对比
| 属性 | 含义 | 是否变化 | 作用 |
|---|---|---|---|
| edge_capacity_t | 边容量上限 | ❌ | 流网络限制 |
| edge_residual_capacity_t | 残量 | ✔ | 算法动态更新 |
| edge_reverse_t | 反向边指针 | ❌ | 找到反向边进行回退 |
三、edge_reverse_t的数据实现方式
3.1 在 adjacency_list 中的实现
structstored_edge{VertexDescriptor target;EdgeProperty property;// 可能嵌套 edge_reverse_t};然后 property_map:
boost::property_map<Graph,edge_reverse_t>::type reverse_edges=get(edge_reverse,g);访问方式:
EdgeDesc rev=reverse_edges[e];e:当前边的 edge_descriptorrev:反向边的 edge_descriptor
算法通过 rev 可以直接访问对端边(残量流更新)
3.2 为什么 adjacency_list 需要 edge_reverse
在 adjacency_list 中:
- edge_descriptor =
(source, index) - 无法自动知道反向边(
target -> source) - 必须显式存储反向边 descriptor
EdgeList: out_edges[source] = [...] EdgeList: out_edges[target] = [...]四、算法如何使用 edge_reverse
4.1 Dinic / Edmonds-Karp / Push-Relabel
- 构建正向边 (e)
- 构建反向边 (e^{rev})
- 将
edge_reverse[e] = e^{rev},edge_reverse[e^{rev}] = e - 算法核心循环:
residual[e]-=delta;residual[reverse_edges[e]]+=delta;- 正向减少流
- 反向增加流(回退)
4.2 代码示例
autoe1=add_edge(u,v,g).first;autoe2=add_edge(v,u,g).first;capacity[e1]=cap;capacity[e2]=0;reverse_edges[e1]=e2;reverse_edges[e2]=e1;注意:反向边容量一般为 0(初始残量网络)
五、edge_reverse_t的核心价值
零开销的泛型抽象
- 算法不关心边存在哪个容器
- 只通过
reverse_edges[e]找到反向边
统一算法接口
- Dinic / Push-Relabel / Max-Flow 都使用同一个接口
内存布局解耦
- 反向边 descriptor 可以存任何类型(pair / struct / index)
六、工程实践
6.1 自定义 Graph 下的实现方式
如果你有自己的 Graph:
structEdge{uint32_tdst;uint32_treverse_id;// 对应 reverse edge 索引doublecapacity;};更新残量流:
auto&e=g.adj[u][i];auto&er=g.adj[e.dst][e.reverse_id];e.capacity-=delta;er.capacity+=delta;完全内建 reverse,无需 property_map
6.2 常见误区
没有建立反向边
正向 / 反向容量设置错误
reverse_edges 和 residual_edges 类型不匹配
忘记双向绑定
七、源码追踪
- Tag 定义
boost/graph/properties.hpp struct edge_reverse_t { typedef edge_property_tag kind; }; BOOST_INSTALL_PROPERTY(edge, reverse);- property_map
boost/graph/detail/adjacency_list.hpp get(edge_reverse, g) → adjacency_list_edge_property_map- 算法层面
- Dinic / push_relabel / edmonds_karp
- 使用 property_map 访问 reverse edge
八、与 SLAM / 因子图的类比
| Flow Graph | SLAM Graph |
|---|---|
| edge_capacity_t | 约束权重上限 |
| edge_reverse_t | 约束残差回传方向 |
| residual_capacity_t | 可调节自由度 |
本质都是“正向约束 + 回退”
九、 总结
boost::edge_reverse_t是边的反向标识 tag- 它是算法与图结构之间的编译期契约
- 实际数据存储在 property_map 中
- 在最大流算法中,核心作用是找到反向边进行流回退
- 工程实践中,你可以自定义 Graph 内建 reverse_id,实现零开销访问
boost::vertex_color_t
一、boost::vertex_color_t是什么?
1 定义
boost::vertex_color_t是一个Vertex Property Tag,用于标记顶点的访问状态(颜色)。
重点:
- 它本身不存储数据
- 只是一个类型标签
- 与 BFS、DFS、拓扑排序等遍历算法强绑定
1.2 数学语义
在图遍历中,通常把顶点标记为三种颜色:
| 颜色 | 含义 |
|---|---|
| white | 未访问(初始状态) |
| gray | 已发现但未完成探索 |
| black | 已完成探索 |
对应 BFS/DFS 算法中的状态标记。
二、vertex_color_t在 Boost.Graph 类型系统中的角色
structvertex_color_t{};- 属于property tag
- 内置成员类型:
kind = vertex_property_tag - 作为编译期契约让算法可以安全访问顶点颜色
2.1 与vertex_index_t对比
| 属性 | 含义 | 是否变化 | 作用 |
|---|---|---|---|
| vertex_index_t | 顶点编号 | ❌ | vertex_descriptor → 索引 |
| vertex_color_t | 顶点颜色 | ✔ | 遍历状态标记 |
三、vertex_color_t的数据实现方式
3.1 在 adjacency_list 中的定义
usingGraph=boost::adjacency_list<boost::vecS,boost::vecS,boost::directedS,boost::property<boost::vertex_color_t,boost::default_color_type>>;含义
- 每个顶点都会有一个颜色字段
- 类型一般为
boost::default_color_type(枚举:white/gray/black)
3.2 property_map
获取颜色 map:
autocolor=get(boost::vertex_color,g);使用方式:
color[u]=boost::gray_color;if(color[v]==boost::white_color)...注意:color_map 是对顶点数组的直接访问器,零开销。
四、算法使用 vertex_color_t
4.1 BFS
boost::breadth_first_search(g,s,boost::visitor(visitor).color_map(color));- 初始状态所有顶点 white
- 发现顶点标记 gray
- 完成访问标记 black
4.2 DFS
boost::depth_first_search(g,boost::visitor(visitor).color_map(color));- DFS 依赖颜色判断是否回退
- 避免重复访问
4.3 拓扑排序 / 强连通分量
- 颜色 map 用于标记访问状态
- 避免重复处理顶点
五、vertex_color_t 的核心价值
算法与图分离
- 算法不关心颜色字段存储在哪
- 通过 property_map 获取
零开销泛型抽象
- 可以替换为 external color map
- 遍历算法依旧正常
允许外部复用
- 例如同一个颜色 map 用于 BFS/DFS/拓扑排序
六、external color map
std::vector<boost::default_color_type>color_storage(num_vertices(g));autocolor=boost::make_iterator_property_map(color_storage.begin(),get(boost::vertex_index,g));- 不在图内部存储颜色
- 支持共享或多次遍历
七、源码追踪
- Tag 定义
boost/graph/properties.hppstructvertex_color_t{typedefvertex_property_tag kind;};BOOST_INSTALL_PROPERTY(vertex,color);- property_map 特化
boost/graph/detail/adjacency_list.hppget(vertex_color,g)->adjacency_list_vertex_property_map- 算法层面
- BFS / DFS / Topological / Strong components
- 都通过
get(color_tag, g)获取颜色 map
八、工程实践
8.1 自定义 Graph 下的实现
structVertex{boost::default_color_type color;};std::vector<Vertex>vertices;autocolor=make_iterator_property_map(vertices.begin(),get(vertex_index,g),&Vertex::color);- 同样可以被 BFS/DFS 使用
- 支持外部复用
- 支持零开销访问
8.2 常见误区
忘记初始化颜色 → 遍历结果不正确
同时用多个遍历共用同一个 color map(未清零)
使用非标准类型(算法只支持 default_color_type 枚举)
九、与 SLAM / 优化的类比
| Boost.Graph | SLAM / 因子图 |
|---|---|
| vertex_color_t | 顶点状态标记(已处理 / 待处理 / 在队列中) |
| white_color | 未优化 / 未处理 |
| gray_color | 当前队列 / 正在优化 |
| black_color | 优化完成 / 固定 |
可以理解为BFS/DFS的访问状态 ≈ SLAM图节点的处理状态
十、 总结
boost::vertex_color_t是顶点颜色的类型标签- 遍历算法的核心依赖
- 通过 property_map 与图解耦
- 可以在内部或外部实现,支持零开销泛型算法
- 工程实践中,必须初始化并正确管理状态