1. 问题描述
本文描述了找出一个有向连通图中所有的环的解决方案
测试用到的有向连通图
2. 自写算法
通过深度优先遍历算法,发现回边时,即存在环的原理来找出环。对于用共享边的环,以下算法有些环找不出来,如上图中的2->8->9->6->2。为什么找不出来,自己走一边深度优先遍历的过程再结合算法原理即可得知。使用的函数为boost::depth_first_search, 具体参看后文算法实现
3. boost自身实现的tiernan算法找环
使用的函数为boost::tiernan_all_cycles, 具体参看后文算法实现。
注:常规写完后,有编译错误,说找不到标识符renumber_vertex_indices,是个bug, 可参看https://github.com/boostorg/graph/issues/182
4. 完整测试代码与结果
cpp
#include <boost/config.hpp>
#include <iostream>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/depth_first_search.hpp>
#include <boost/graph/tiernan_all_cycles.hpp>
#include <vector>
#include <map>
#include <stack>
using namespace std;
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::bidirectionalS> MyGraph;
//只实现了有向图的找环,对于用共享边的环,以下算法有些环找不出来
//boost::add_edge(0, 1, g);
//boost::add_edge(1, 2, g);
//boost::add_edge(2, 3, g);
//boost::add_edge(3, 4, g);
//boost::add_edge(4, 5, g);
//boost::add_edge(4, 2, g);
//boost::add_edge(6, 2, g);
//boost::add_edge(7, 6, g);
//boost::add_edge(4, 7, g);
//boost::add_edge(2, 8, g);
//boost::add_edge(8, 9, g);
//boost::add_edge(9, 6, g);
struct FindCycleVisitor : public boost::default_dfs_visitor
{
std::stack<MyGraph::vertex_descriptor> m_stack;
std::vector<MyGraph::vertex_descriptor> m_cycle;
FindCycleVisitor(){}
template <class Vertex, class Graph>
void discover_vertex(Vertex u, const Graph& g)
{
m_stack.push(u);
}
template <class Edge, class Graph>
void back_edge(Edge e, const Graph& g) {
auto u = boost::source(e, g);
auto v = boost::target(e, g);
// Find the top of the stack that is an ancestor of v
std::stack<MyGraph::vertex_descriptor> temp_stack = m_stack;
while (!temp_stack.empty() && temp_stack.top() != v) {
m_cycle.push_back(temp_stack.top());
temp_stack.pop();
}
m_cycle.push_back(v); // Include v in the cycle
// Optionally, print or store the cycle
std::cout << "Cycle found: ";
for (auto it = m_cycle.crbegin(); it != m_cycle.crend(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
// Clear the cycle for the next detection (optional)
m_cycle.clear();
}
template <class Vertex, class Graph>
void finish_vertex(Vertex v, const Graph& g) {
m_stack.pop();
}
};
// 自定义访问者,用于打印找到的环
// see https://github.com/boostorg/graph/issues/182
namespace boost
{
template<typename Graph>
void renumber_vertex_indices(Graph const&) {}
}
struct TiernanCycleVisitor {
TiernanCycleVisitor(){}
template <typename Graph>
void cycle(const std::vector<typename Graph::vertex_descriptor>& path, Graph g) const
{
for (Graph::vertex_descriptor v : path)
{
std::cout << v << " ";
}
std::cout << std::endl;
}
};
int main(int argc, char** argv)
{
MyGraph g;
boost::add_edge(0, 1, g);
boost::add_edge(1, 2, g);
boost::add_edge(2, 3, g);
boost::add_edge(3, 4, g);
boost::add_edge(4, 5, g);
boost::add_edge(4, 2, g);
boost::add_edge(6, 2, g);
boost::add_edge(7, 6, g);
boost::add_edge(4, 7, g);
boost::add_edge(2, 8, g);
boost::add_edge(8, 9, g);
boost::add_edge(9, 6, g);
//自写算法查找环
std::cout << "自写算法查找环:" << std::endl;
FindCycleVisitor vis;
boost::depth_first_search(g, boost::visitor(vis));
// 使用tiernan_all_cycles算法查找所有环
std::cout << "tiernan算法查找环:" << std::endl;
TiernanCycleVisitor tiernanVis;
boost::tiernan_all_cycles(g, tiernanVis);
return 0;
}