接下来一段日子,会写一些跟bgl相关的内容。bgl是一个性能很不错的库,但是源码跟鬼畜一样…文档也写得很乱。因为最近做的论文需要比较好的性能,实在不得不硬着头皮重新用起bgl。所以尽可能的做一些总结,希望能给后面需要使用的同学能有点帮助。
开始: 一个简单的小例子
1 |
|
基本概念
Vertex Descriptors 与 Edge Descriptors
在BGL里面,我们需要对点和边进行相应的操作。于是BGL定义了点描述符和边描述符,也可以称为句柄。要对一个点或者边进行操作,我们需要用过句柄(描述符)进行操作。对于一个图,要获取描述符的类型,只能通过一个graph_traits的类进行获取。graph_traits是对图类型进行描述的一个类,包含了图结构的各种信息。
1 | template <typename Graph> bool is_self_loop(typename graph_traits<Graph>::edge_descriptor e, const Graph& g) |
上面这个例子是判断一条边是不是自环。函数传进两个参数,第一个参数是边的修饰符,第二个参数是图。line 3创建两个点修饰符,source和target分别是获取边修饰符所代表的边的首尾两个顶点的修饰符。最后判断是否相同。
Property Maps
在BGL里面,另外比较重要的一个结构是Property Maps。这个概念可以理解成点或者边上的属性,比如点上可以有attribute(attribute graph),边上可以有权重。当然,用户也可以在自己的代码里面自己定义,比如存在hashmap。但是BGL提供了这样一个统一的结构,用户就可以写一些可以通用的算法。
1
2
3
4
5template <typename VertexDescriptor, typename VertexNameMap> void
print_vertex_name(VertexDescriptor v, VertexNameMap name_map)
{
std::cout << get(name_map, v);
}图的访问/遍历
BGL提供了五种方式对图的访问,都是通过迭代器来完成的。
a. 点迭代器(vertex iterator):迭代器里面的元素是点描述符(点句柄)。
b. 边迭代器(edge iterator):迭代器里面的元素是边描述符(边句柄)。
c. 出边迭代器(out-edge iterator)。用来访问某个点的所有出边的迭代器,迭代器里面的元素是边描述符(边句柄)。
d. 入边迭代器(in-edge iterator)。用来访问某个点的所有入边的迭代器,迭代器里面的元素是边描述符(边句柄)。
e. 邻接迭代器(adjacency iterator)。用来访问一个顶点的所有邻接点。迭代器里面的元素是点描述符(点句柄)。
跟描述符一样,迭代器的类型也是通过graph_traits这个类来确定与获取。1
2
3
4
5
6
7
8
9
10
11
12template <typename Graph, typename VertexNameMap> void print_vertex_names(const Graph& g, VertexNameMap name_map)
{
std::cout << "vertices(g) = { ";
typedef typename graph traits<Graph>::vertex iterator_iter t;
for (std::pair<iter_t, iter_t> p = vertices(g);
p.first != p.second; ++p.first)
{
print_vertex_name(*p.first, name_map);
std::cout << ’ ’;
}
std::cout << "}" << std::endl;
}
- 图的创建与修改
下面这个例子添加了五个点,同时添加了七条边。
a. 添加点
add_vertex 传入的参数是graph,返回是一个点描述符。
a. 添加边
add_edge(a, b, g)传入的参数是首尾两个点的描述符,第三个参数是要插边的图。返回一个tie,tie中第一个元素是边描述符,第二个元素表示是否成功插入图中。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20template <typename Graph, typename VertexNameMap, typename TransDelayMap> void build_router_network(Graph& g, VertexNameMap name_map, TransDelayMap delay_map)
{
typename graph traits<Graph>::vertex descriptor a, b, c, d, e;
a = add_vertex(g); name_map[a] = ’a’;
b = add_vertex(g); name_map[b] = ’b’;
c = add_vertex(g); name_map[c] = ’c’;
d = add_vertex(g); name_map[d] = ’d’;
e = add_vertex(g); name_map[e] = ’e’;
typename graph traits<Graph>::edge_descriptor ed;
bool inserted;
tie(ed, inserted) = add_edge(a, b, g); delay_map[ed] = 1.2;
tie(ed, inserted) = add_edge(a, d, g); delay_map[ed] = 4.5;
tie(ed, inserted) = add_edge(b, d, g); delay_map[ed] = 1.8;
tie(ed, inserted) = add_edge(c, a, g); delay_map[ed] = 2.6;
tie(ed, inserted) = add_edge(c, e, g); delay_map[ed] = 5.2;
tie(ed, inserted) = add_edge(d, c, g);
delay_map[ed] = 0.4;
tie(ed, inserted) = add_edge(d, e, g);
delay_map[ed] = 3.3;
}
- 算法访问器(Algorithm Visitors)
这个机制有点像排序中的compare()函数。对于内置的算法,会作为参数把函数传递进去,进而执行特定的操作。比如我们有一个任务,需要对图进行宽度优先遍历。bgl是提供宽度优先遍历的算法。但是如果我们要输出每个顶点的名字,就需要我们提供一个vistor的函数。下面是一个vistor的例子。
1 | template <typename VertexNameMap> class bfs name printer : public default_bfs_visitor |
当用户调用宽度优先遍历的函数时,可以如下将vistor传递进去。
1 | bfs_name_printer<VertexNameMap> vis(name_map); |