是否可以将boost库的广度优先搜索算法应用于矩阵
Is it possible to apply breadth-first search algorithm of boost library to matrix?
我的任务是找到矩阵中从一点到另一点的最短路径。只能在这样的方向(向上、向下、向左、向右)上移动。
0 0 0 0 1 0 0 0
1 0 0 0 0 0 0 0
0 0 0 1 0 1 F 0
0 1 0 1 0 0 0 0
0 0 0 1 0 0 0 0
0 S 0 1 0 0 1 0
0 0 0 0 0 0 1 0
0 0 0 0 0 0 1 0
S-起点
F-目的地(终点)
0-自由细胞(我们可以穿过它们)
1-"墙"(我们无法穿过它们)
很明显,广度优先搜索以最优化的方式解决了这个问题。我知道Boost库提供了这个算法,但我以前没有Boost。
在我的案例中,如何使用Boost进行广度优先搜索?据我所知,Boost的广度优先搜索算法仅适用于图形。我认为将矩阵转换为具有m*n
顶点和m*(n -1) + (m-1)*n
边的图不是一个好主意。
我可以将广度优先搜索算法应用于矩阵(而不将其转换为图),还是更好地实现我自己的广度优先搜索功能?
(提前为这个答案的长度道歉。我已经有一段时间没有使用BGL了,我认为这会是一个很好的复习。完整的代码在这里。)
Boost图库(以及一般的通用编程)的美妙之处在于,您不需要使用任何特定的数据结构来利用给定的算法。您提供的矩阵以及遍历它的规则已经定义了一个图。所需要的只是将这些规则编码在一个可以用来利用BGL算法的traits类中。
具体来说,我们要做的是为图定义boost::graph_traits<T>
的专门化。假设您的矩阵是int
的行中主格式的单个数组。不幸的是,将graph_traits
专门化为int[N]
是不够的,因为它没有提供任何关于矩阵维度的信息。因此,让我们定义您的图形如下:
namespace matrix
{
typedef int cell;
static const int FREE = 0;
static const int WALL = 1;
template< size_t ROWS, size_t COLS >
struct graph
{
cell cells[ROWS*COLS];
};
}
我在这里使用了单元格数据的组合,但如果要从外部管理指针,您也可以很容易地使用它。现在我们有一个用矩阵维度编码的类型,可以用来专门化graph_traits
。但首先让我们定义一些我们需要的函数和类型。
顶点类型和辅助功能:
namespace matrix
{
typedef size_t vertex_descriptor;
template< size_t ROWS, size_t COLS >
size_t get_row(
vertex_descriptor vertex,
graph< ROWS, COLS > const & )
{
return vertex / COLS;
}
template< size_t ROWS, size_t COLS >
size_t get_col(
vertex_descriptor vertex,
graph< ROWS, COLS > const & )
{
return vertex % COLS;
}
template< size_t ROWS, size_t COLS >
vertex_descriptor make_vertex(
size_t row,
size_t col,
graph< ROWS, COLS > const & )
{
return row * COLS + col;
}
}
遍历顶点的类型和函数:
namespace matrix
{
typedef const cell * vertex_iterator;
template< size_t ROWS, size_t COLS >
std::pair< vertex_iterator, vertex_iterator >
vertices( graph< ROWS, COLS > const & g )
{
return std::make_pair( g.cells, g.cells + ROWS*COLS );
}
typedef size_t vertices_size_type;
template< size_t ROWS, size_t COLS >
vertices_size_type
num_vertices( graph< ROWS, COLS > const & g )
{
return ROWS*COLS;
}
}
边缘类型:
namespace matrix
{
typedef std::pair< vertex_descriptor, vertex_descriptor > edge_descriptor;
bool operator==(
edge_descriptor const & lhs,
edge_descriptor const & rhs )
{
return
lhs.first == rhs.first && lhs.second == rhs.second ||
lhs.first == rhs.second && lhs.second == rhs.first;
}
bool operator!=(
edge_descriptor const & lhs,
edge_descriptor const & rhs )
{
return !(lhs == rhs);
}
}
最后,迭代器和函数可以帮助我们遍历顶点和边之间的关联关系:
namespace matrix
{
template< size_t ROWS, size_t COLS >
vertex_descriptor
source(
edge_descriptor const & edge,
graph< ROWS, COLS > const & )
{
return edge.first;
}
template< size_t ROWS, size_t COLS >
vertex_descriptor
target(
edge_descriptor const & edge,
graph< ROWS, COLS > const & )
{
return edge.second;
}
typedef boost::shared_container_iterator< std::vector< edge_descriptor > > out_edge_iterator;
template< size_t ROWS, size_t COLS >
std::pair< out_edge_iterator, out_edge_iterator >
out_edges(
vertex_descriptor vertex,
graph< ROWS, COLS > const & g )
{
boost::shared_ptr< std::vector< edge_descriptor > > edges( new std::vector< edge_descriptor >() );
if( g.cells[vertex] == FREE )
{
size_t
row = get_row( vertex, g ),
col = get_col( vertex, g );
if( row != 0 )
{
vertex_descriptor up = make_vertex( row - 1, col, g );
if( g.cells[up] == FREE )
edges->push_back( edge_descriptor( vertex, up ) );
}
if( row != ROWS-1 )
{
vertex_descriptor down = make_vertex( row + 1, col, g );
if( g.cells[down] == FREE )
edges->push_back( edge_descriptor( vertex, down ) );
}
if( col != 0 )
{
vertex_descriptor left = make_vertex( row, col - 1, g );
if( g.cells[left] == FREE )
edges->push_back( edge_descriptor( vertex, left ) );
}
if( col != COLS-1 )
{
vertex_descriptor right = make_vertex( row, col + 1, g );
if( g.cells[right] == FREE )
edges->push_back( edge_descriptor( vertex, right ) );
}
}
return boost::make_shared_container_range( edges );
}
typedef size_t degree_size_type;
template< size_t ROWS, size_t COLS >
degree_size_type
out_degree(
vertex_descriptor vertex,
graph< ROWS, COLS > const & g )
{
std::pair< out_edge_iterator, out_edge_iterator > edges = out_edges( vertex, g );
return std::distance( edges.first, edges.second );
}
}
现在我们准备定义boost::graph_traits
:的专业化
namespace boost
{
template< size_t ROWS, size_t COLS >
struct graph_traits< matrix::graph< ROWS, COLS > >
{
typedef matrix::vertex_descriptor vertex_descriptor;
typedef matrix::edge_descriptor edge_descriptor;
typedef matrix::out_edge_iterator out_edge_iterator;
typedef matrix::vertex_iterator vertex_iterator;
typedef boost::undirected_tag directed_category;
typedef boost::disallow_parallel_edge_tag edge_parallel_category;
struct traversal_category :
virtual boost::vertex_list_graph_tag,
virtual boost::incidence_graph_tag {};
typedef matrix::vertices_size_type vertices_size_type;
typedef matrix::degree_size_type degree_size_type;
static vertex_descriptor null_vertex() { return ROWS*COLS; }
};
}
以下是如何执行广度优先搜索并找到最短路径:
int main()
{
const size_t rows = 8, cols = 8;
using namespace matrix;
typedef graph< rows, cols > my_graph;
my_graph g =
{
FREE, FREE, FREE, FREE, WALL, FREE, FREE, FREE,
WALL, FREE, FREE, FREE, FREE, FREE, FREE, FREE,
FREE, FREE, FREE, WALL, FREE, WALL, FREE, FREE,
FREE, WALL, FREE, WALL, FREE, FREE, FREE, FREE,
FREE, FREE, FREE, WALL, FREE, FREE, FREE, FREE,
FREE, FREE, FREE, WALL, FREE, FREE, WALL, FREE,
FREE, FREE, FREE, FREE, FREE, FREE, WALL, FREE,
FREE, FREE, FREE, FREE, FREE, FREE, WALL, FREE,
};
const vertex_descriptor
start_vertex = make_vertex( 5, 1, g ),
finish_vertex = make_vertex( 2, 6, g );
vertex_descriptor predecessors[rows*cols] = { 0 };
using namespace boost;
breadth_first_search(
g,
start_vertex,
visitor( make_bfs_visitor( record_predecessors( predecessors, on_tree_edge() ) ) ).
vertex_index_map( identity_property_map() ) );
typedef std::list< vertex_descriptor > path;
path p;
for( vertex_descriptor vertex = finish_vertex; vertex != start_vertex; vertex = predecessors[vertex] )
p.push_front( vertex );
p.push_front( start_vertex );
for( path::const_iterator cell = p.begin(); cell != p.end(); ++cell )
std::cout << "[" << get_row( *cell, g ) << ", " << get_col( *cell, g ) << "]n" ;
return 0;
}
它沿着从开始到结束的最短路径输出单元格:
[5, 1]
[4, 1]
[4, 2]
[3, 2]
[2, 2]
[1, 2]
[1, 3]
[1, 4]
[1, 5]
[1, 6]
[2, 6]
您绝对可以使用Boost Graph库!这个库中的算法的实现方式是从任何图形数据结构中抽象出来,而不是用迭代器来操作。例如,要从一个节点移动到另一个节点,算法使用邻接迭代器。你基本上会查看一个特定的算法,例如BFS,并找出这个算法需要什么概念:在这种情况下,你与它一起使用的图需要是"顶点列表图"answers"关联图"。请注意,这些不是具体的类,而是概念:它们指定了如何访问数据结构。该算法还采用了许多额外的参数,如起始节点和属性映射来标记(颜色)已经访问的节点。
要将该算法与矩阵一起使用,您需要为该算法提供矩阵的"图形视图":节点与其直接邻居相邻,除非相应的邻居设置为1(而且,很明显,您不会偏离矩阵的边)。创建这样的图并不完全是琐碎的,但我认为了解Boost graph库的工作原理非常有用:即使你不想使用这个特定的库,这是一个很好的例子,说明了如何在抽象中实现算法,使算法即使在完全不可预见的情况下也适用(好吧,我有偏见:早在Jeremy创建Boost Graph库之前,我就已经写了关于大致相同的东西的毕业论文,我们提出了基本相同的抽象)。
尽管如此,我认为使用广度优先搜索可能不值得学习Boost Graph库:这是一个非常琐碎的算法,您可能只想直接实现它。此外,这看起来很像家庭作业,在这种情况下,你可能要自己实现算法。尽管使用Boost Graph库可能会给人留下深刻印象,但您的导师可能不会这么认为。我认为更令人印象深刻的是,像Boost Graph库那样,以独立于数据结构的风格实现BFS,然后使用它。在Boost Graph库的指导下,这绝对是可行的,即使是作为一种练习(尽管可能比所需的工作量更多)。顺便说一句,是的,我可以发布代码,但不,我不会。不过,我很乐意帮助解决发布的具体问题。
- 提升图广度优先搜索前置编译错误
- 使用迭代深度优先搜索算法的未加权图的最短路径
- 使用openmp实现并行广度优先搜索
- 广度优先搜索陷入无限循环
- 图上的深度优先搜索算法中的内存泄漏
- 并行化广度优先搜索
- 如何使用C 中的广度优先搜索解决8个式嘴问题
- 如何使用广度优先搜索确定是否可以在有向图中到达顶点
- 修改广度优先搜索算法以记住矩阵中的最短路径
- 如何实现广度优先搜索
- 图论:广度优先搜索
- 广度优先搜索未找到正确的路径
- 有向图中的广度优先搜索
- 使用Boost图库从多个来源进行广度优先搜索
- 如何用C++编写广度优先搜索的代码
- C++以广度优先搜索的方式查找多个数组中元素的所有组合
- 广度优先搜索:有向图
- 邻接列表,用于创建图形和广度优先搜索 (BFS) 和 Fepth First Search (DFS)
- 是否可以将boost库的广度优先搜索算法应用于矩阵
- 调试使用广度优先搜索(BFS)的简单图算法