From bee272b4fdc8273e93f7b094b94716598b5676b4 Mon Sep 17 00:00:00 2001 From: Laurent Perron Date: Mon, 7 Nov 2022 16:38:45 +0100 Subject: [PATCH] [GRAPH] check connectivity in the eulerian_path --- ortools/graph/eulerian_path.h | 63 ++++++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 16 deletions(-) diff --git a/ortools/graph/eulerian_path.h b/ortools/graph/eulerian_path.h index c08d783673..b1ce9edac5 100644 --- a/ortools/graph/eulerian_path.h +++ b/ortools/graph/eulerian_path.h @@ -35,25 +35,29 @@ namespace operations_research { +namespace internal { +template +bool GraphIsConnected(const Graph& graph); +} // namespace internal + // Returns true if a graph is Eulerian, aka all its nodes are of even degree. template -bool IsEulerianGraph(const Graph& graph) { +bool IsEulerianGraph(const Graph& graph, bool assume_connectivity = true) { typedef typename Graph::NodeIndex NodeIndex; for (const NodeIndex node : graph.AllNodes()) { if ((graph.OutDegree(node) + graph.InDegree(node)) % 2 != 0) { return false; } } - // TODO(user): Check graph connectivity. - return true; + return assume_connectivity || internal::GraphIsConnected(graph); } // Returns true if a graph is Semi-Eulerian, aka at most two of its nodes are of // odd degree. // odd_nodes is filled with odd nodes of the graph. template -bool IsSemiEulerianGraph(const Graph& graph, - std::vector* odd_nodes) { +bool IsSemiEulerianGraph(const Graph& graph, std::vector* odd_nodes, + bool assume_connectivity = true) { CHECK(odd_nodes != nullptr); for (const NodeIndex node : graph.AllNodes()) { const int degree = graph.OutDegree(node) + graph.InDegree(node); @@ -61,8 +65,8 @@ bool IsSemiEulerianGraph(const Graph& graph, odd_nodes->push_back(node); } } - // TODO(user): Check graph connectivity. - return odd_nodes->size() <= 2; + if (odd_nodes->size() > 2) return false; + return assume_connectivity || internal::GraphIsConnected(graph); } // Builds an Eulerian path/trail on an undirected graph starting from node root. @@ -111,12 +115,11 @@ std::vector BuildEulerianPathFromNode(const Graph& graph, // This function works only on Reverse graphs // (cf. ortools/graph/graph.h). // Returns an empty tour if either root is invalid or if a tour cannot be built. -// As of 10/2015, assumes the graph is connected. template -std::vector BuildEulerianTourFromNode(const Graph& graph, - NodeIndex root) { +std::vector BuildEulerianTourFromNode( + const Graph& graph, NodeIndex root, bool assume_connectivity = true) { std::vector tour; - if (IsEulerianGraph(graph)) { + if (IsEulerianGraph(graph, assume_connectivity)) { tour = BuildEulerianPathFromNode(graph, root); } return tour; @@ -125,26 +128,54 @@ std::vector BuildEulerianTourFromNode(const Graph& graph, // Same as above but without specifying a start/end root node (node 0 is taken // as default root). template -std::vector BuildEulerianTour(const Graph& graph) { - return BuildEulerianTourFromNode(graph, 0); +std::vector BuildEulerianTour( + const Graph& graph, bool assume_connectivity = true) { + return BuildEulerianTourFromNode(graph, 0, assume_connectivity); } // Builds an Eulerian path/trail on an undirected graph. // This function works only on Reverse graphs // (cf. ortools/graph/graph.h). // Returns an empty tour if a tour cannot be built. -// As of 10/2015, assumes the graph is connected. template -std::vector BuildEulerianPath(const Graph& graph) { +std::vector BuildEulerianPath( + const Graph& graph, bool assume_connectivity = true) { typedef typename Graph::NodeIndex NodeIndex; std::vector path; std::vector roots; - if (IsSemiEulerianGraph(graph, &roots)) { + if (IsSemiEulerianGraph(graph, &roots, assume_connectivity)) { const NodeIndex root = roots.empty() ? 0 : roots.back(); path = BuildEulerianPathFromNode(graph, root); } return path; } + +namespace internal { +template +bool GraphIsConnected(const Graph& graph) { + typedef typename Graph::NodeIndex NodeIndex; + const NodeIndex n = graph.num_nodes(); + if (n <= 1) return true; + // We use iterative DFS, which is probably the fastest. + NodeIndex num_visited = 1; + std::vector stack = {0}; + std::vector visited(n, false); + while (!stack.empty()) { + const NodeIndex node = stack.back(); + stack.pop_back(); + for (auto arc : graph.OutgoingOrOppositeIncomingArcs(node)) { + const NodeIndex neigh = graph.Head(arc); + if (!visited[neigh]) { + visited[neigh] = true; + stack.push_back(neigh); + if (++num_visited == n) return true; + } + } + } + return false; +} +} // namespace internal + } // namespace operations_research #endif // OR_TOOLS_GRAPH_EULERIAN_PATH_H_