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 类型系统中的角色
cpp
struct edge_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 中的实现
cpp
struct stored_edge {
VertexDescriptor target;
EdgeProperty property; // 可能嵌套 edge_reverse_t
};
然后 property_map:
cpp
boost::property_map<Graph, edge_reverse_t>::type reverse_edges = get(edge_reverse, g);
访问方式:
cpp
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
text
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 - 算法核心循环:
cpp
residual[e] -= delta;
residual[reverse_edges[e]] += delta;
- 正向减少流
- 反向增加流(回退)
4.2 代码示例
cpp
auto e1 = add_edge(u, v, g).first;
auto e2 = 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:
cpp
struct Edge {
uint32_t dst;
uint32_t reverse_id; // 对应 reverse edge 索引
double capacity;
};
更新残量流:
cpp
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 定义
text
boost/graph/properties.hpp
struct edge_reverse_t { typedef edge_property_tag kind; };
BOOST_INSTALL_PROPERTY(edge, reverse);
- property_map
text
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 类型系统中的角色
cpp
struct vertex_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 中的定义
cpp
using Graph = 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:
cpp
auto color = get(boost::vertex_color, g);
使用方式:
cpp
color[u] = boost::gray_color;
if(color[v] == boost::white_color) ...
注意:color_map 是对顶点数组的 直接访问器,零开销。
四、算法使用 vertex_color_t
4.1 BFS
cpp
boost::breadth_first_search(g, s,
boost::visitor(visitor)
.color_map(color)
);
- 初始状态所有顶点 white
- 发现顶点标记 gray
- 完成访问标记 black
4.2 DFS
cpp
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
cpp
std::vector<boost::default_color_type> color_storage(num_vertices(g));
auto color = boost::make_iterator_property_map(color_storage.begin(), get(boost::vertex_index, g));
- 不在图内部存储颜色
- 支持共享或多次遍历
七、源码追踪
- Tag 定义
cpp
boost/graph/properties.hpp
struct vertex_color_t { typedef vertex_property_tag kind; };
BOOST_INSTALL_PROPERTY(vertex, color);
- property_map 特化
cpp
boost/graph/detail/adjacency_list.hpp
get(vertex_color, g) -> adjacency_list_vertex_property_map
- 算法层面
- BFS / DFS / Topological / Strong components
- 都通过
get(color_tag, g)获取颜色 map
八、工程实践
8.1 自定义 Graph 下的实现
cpp
struct Vertex {
boost::default_color_type color;
};
std::vector<Vertex> vertices;
auto color = 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 与图解耦
- 可以在内部或外部实现,支持零开销泛型算法
- 工程实践中,必须初始化并正确管理状态