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. 完整测试代码与结果
#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;}