OR-Tools  9.3
topologicalsorter.h
Go to the documentation of this file.
1// Copyright 2010-2021 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14// TopologicalSorter provides topologically sorted traversal of the
15// nodes of a directed acyclic graph (DAG) with up to INT_MAX nodes.
16// It sorts ancestor nodes before their descendants.
17//
18// If your graph is not a DAG and you're reading this, you are probably
19// looking for ortools/graph/strongly_connected_components.h which does
20// the topological decomposition of a directed graph.
21//
22// EXAMPLE:
23//
24// vector<int> result;
25// vector<string> nodes = {"a", "b", "c"};
26// vector<pair<string, string>> arcs = {{"a", "c"}, {"a", "b"}, {"b", "c"}};
27// if (util::StableTopologicalSort(num_nodes, arcs, &result)) {
28// LOG(INFO) << "The topological order is: " << gtl::LogContainer(result);
29// } else {
30// LOG(INFO) << "The graph is cyclic.";
31// // Note: you can extract a cycle with the TopologicalSorter class, or
32// // with the API defined in circularity_detector.h.
33// }
34// // This will be successful and result will be equal to {"a", "b", "c"}.
35//
36// There are 8 flavors of topological sort, from these 3 bits:
37// - There are OrDie() versions that directly return the topological order, or
38// crash if a cycle is detected (and LOG the cycle).
39// - There are type-generic versions that can take any node type (including
40// non-dense integers), but slower, or the "dense int" versions which requires
41// nodes to be a dense interval [0..num_nodes-1]. Note that the type must
42// be compatible with LOG << T if you're using the OrDie() version.
43// - The sorting can be either stable or not. "Stable" essentially means that it
44// will preserve the order of nodes, if possible. More precisely, the returned
45// topological order will be the lexicographically minimal valid order, where
46// "lexicographic" applies to the indices of the nodes.
47//
48// TopologicalSort()
49// TopologicalSortOrDie()
50// StableTopologicalSort()
51// StableTopologicalSortOrDie()
52// DenseIntTopologicalSort()
53// DenseIntTopologicalSortOrDie()
54// DenseIntStableTopologicalSort()
55// DenseIntStableTopologicalSortOrDie()
56//
57// If you need more control, or a step-by-step topological sort, see the
58// TopologicalSorter classes below.
59
60#ifndef UTIL_GRAPH_TOPOLOGICALSORTER_H__
61#define UTIL_GRAPH_TOPOLOGICALSORTER_H__
62
63#include <queue>
64#include <type_traits>
65#include <vector>
66
67#include "absl/base/attributes.h"
68#include "absl/container/flat_hash_map.h"
71#include "ortools/base/macros.h"
74
75namespace util {
76
77// Returns true if the graph was a DAG, and outputs the topological order in
78// "topological_order". Returns false if the graph is cyclic.
79// Works in O(num_nodes + arcs.size()), and is pretty fast.
80inline ABSL_MUST_USE_RESULT bool DenseIntTopologicalSort(
81 int num_nodes, const std::vector<std::pair<int, int>>& arcs,
82 std::vector<int>* topological_order);
83
84// Like DenseIntTopologicalSort, but stable.
85inline ABSL_MUST_USE_RESULT bool DenseIntStableTopologicalSort(
86 int num_nodes, const std::vector<std::pair<int, int>>& arcs,
87 std::vector<int>* topological_order);
88
89// Finds a cycle in the directed graph given as argument: nodes are dense
90// integers in 0..num_nodes-1, and (directed) arcs are pairs of nodes
91// {from, to}.
92// The returned cycle is a list of nodes that form a cycle, eg. {1, 4, 3}
93// if the cycle 1->4->3->1 exists.
94// If the graph is acyclic, returns an empty vector.
95ABSL_MUST_USE_RESULT std::vector<int> FindCycleInDenseIntGraph(
96 int num_nodes, const std::vector<std::pair<int, int>>& arcs);
97
98// Like the two above, but with generic node types. The nodes must be provided.
99// Can be significantly slower, but still linear.
100template <typename T>
101ABSL_MUST_USE_RESULT bool TopologicalSort(
102 const std::vector<T>& nodes, const std::vector<std::pair<T, T>>& arcs,
103 std::vector<T>* topological_order);
104template <typename T>
105ABSL_MUST_USE_RESULT bool StableTopologicalSort(
106 const std::vector<T>& nodes, const std::vector<std::pair<T, T>>& arcs,
107 std::vector<T>* topological_order);
108
109// "OrDie()" versions of the 4 functions above. Those directly return the
110// topological order, which makes their API even simpler.
111inline std::vector<int> DenseIntTopologicalSortOrDie(
112 int num_nodes, const std::vector<std::pair<int, int>>& arcs);
113inline std::vector<int> DenseIntStableTopologicalSortOrDie(
114 int num_nodes, const std::vector<std::pair<int, int>>& arcs);
115template <typename T>
116std::vector<T> TopologicalSortOrDie(const std::vector<T>& nodes,
117 const std::vector<std::pair<T, T>>& arcs);
118template <typename T>
119std::vector<T> StableTopologicalSortOrDie(
120 const std::vector<T>& nodes, const std::vector<std::pair<T, T>>& arcs);
121
122namespace internal {
123// Internal wrapper around the *TopologicalSort classes.
124template <typename T, typename Sorter>
125ABSL_MUST_USE_RESULT bool RunTopologicalSorter(
126 Sorter* sorter, const std::vector<std::pair<T, T>>& arcs,
127 std::vector<T>* topological_order_or_cycle);
128
129// Do not use the templated class directly, instead use one of the
130// typedefs DenseIntTopologicalSorter or DenseIntStableTopologicalSorter.
131//
132// The equivalent of a TopologicalSorter<int> which nodes are the
133// N integers from 0 to N-1 (see the toplevel comment). The API is
134// exactly similar to that of TopologicalSorter, please refer to the
135// TopologicalSorter class below for more detailed comments.
136//
137// If the template parameter is true then the sort will be stable.
138// This means that the order of the nodes will be maintained as much as
139// possible. A non-stable sort is more efficient, since the complexity
140// of getting the next node is O(1) rather than O(log(Nodes)).
141template <bool stable_sort = false>
143 public:
144 // To store the adjacency lists efficiently.
145 typedef std::vector<int> AdjacencyList;
146
147 // For efficiency, it is best to specify how many nodes are required
148 // by using the next constructor.
150 : traversal_started_(false),
151 num_edges_(0),
152 num_edges_added_since_last_duplicate_removal_(0) {}
153
154 // One may also construct a DenseIntTopologicalSorterTpl with a predefined
155 // number of empty nodes. One can thus bypass the AddNode() API,
156 // which may yield a lower memory usage.
157 explicit DenseIntTopologicalSorterTpl(int num_nodes)
158 : adjacency_lists_(num_nodes),
159 traversal_started_(false),
160 num_edges_(0),
161 num_edges_added_since_last_duplicate_removal_(0) {}
162
163 // Performs in constant amortized time. Calling this will make all
164 // node indices in [0 .. node_index] be valid node indices. If you
165 // can avoid using AddNode(), you should! If you know the number of
166 // nodes in advance, you should specify that at construction time --
167 // it will be faster and use less memory.
168 void AddNode(int node_index);
169
170 // Performs in constant amortized time. Calling this will make all
171 // node indices in [0, max(from, to)] be valid node indices.
172 void AddEdge(int from, int to);
173
174 // Performs in O(average degree) in average. If a cycle is detected
175 // and "output_cycle_nodes" isn't NULL, it will require an additional
176 // O(number of edges + number of nodes in the graph) time.
177 bool GetNext(int* next_node_index, bool* cyclic,
178 std::vector<int>* output_cycle_nodes = nullptr);
179
182 return nodes_with_zero_indegree_.size();
183 }
184
185 void StartTraversal();
186
187 bool TraversalStarted() const { return traversal_started_; }
188
189 // Given a vector<AdjacencyList> of size n such that elements of the
190 // AdjacencyList are in [0, n-1], remove the duplicates within each
191 // AdjacencyList of size greater or equal to skip_lists_smaller_than,
192 // in linear time. Returns the total number of duplicates removed.
193 // This method is exposed for unit testing purposes only.
194 static int RemoveDuplicates(std::vector<AdjacencyList>* lists,
195 int skip_lists_smaller_than);
196
197 // To extract a cycle. When there is no cycle, cycle_nodes will be empty.
198 void ExtractCycle(std::vector<int>* cycle_nodes) const;
199
200 private:
201 // Outgoing adjacency lists.
202 std::vector<AdjacencyList> adjacency_lists_;
203
204 bool traversal_started_;
205
206 // Only valid after a traversal started.
207 int num_nodes_left_;
208 typename std::conditional<
209 stable_sort,
210 // We use greater<int> so that the lowest elements gets popped first.
211 std::priority_queue<int, std::vector<int>, std::greater<int>>,
212 std::queue<int>>::type nodes_with_zero_indegree_;
213 std::vector<int> indegree_;
214
215 // Used internally by AddEdge() to decide whether to trigger
216 // RemoveDuplicates(). See the .cc.
217 int num_edges_; // current total number of edges.
218 int num_edges_added_since_last_duplicate_removal_;
219
220 private:
221 DISALLOW_COPY_AND_ASSIGN(DenseIntTopologicalSorterTpl);
222};
223
224extern template class DenseIntTopologicalSorterTpl<false>;
225extern template class DenseIntTopologicalSorterTpl<true>;
226
227} // namespace internal
228
229// Recommended version for general usage. The stability makes it more
230// deterministic, and its behavior is guaranteed to never change.
232 /*stable_sort=*/true>
234
235// Use this version if you are certain you don't care about the
236// tie-breaking order and need the 5 to 10% performance gain. The
237// performance gain can be more significant for large graphs with large
238// numbers of source nodes (for example 2 Million nodes with 2 Million
239// random edges sees a factor of 0.7 difference in completion time).
241 /*stable_sort=*/false>
243
244// A copy of each Node is stored internally. Duplicated edges are allowed,
245// and discarded lazily so that AddEdge() keeps an amortized constant
246// time, yet the total memory usage remains O(number of different edges +
247// number of nodes).
248//
249// DenseIntTopologicalSorter implements the core topological sort
250// algorithm. For greater efficiency it can be used directly
251// (TopologicalSorter<int> is about 1.5-3x slower).
252//
253// TopologicalSorter requires that all nodes and edges be added before
254// traversing the nodes, otherwise it will die with a fatal error.
255//
256//
257// Note(user): since all the real work is done by
258// DenseIntTopologicalSorterTpl, and this class is a template, we inline
259// every function here in the .h.
260//
261// If stable_sort is true then the topological sort will preserve the
262// original order of the nodes as much as possible. Note, the order
263// which is preserved is the order in which the nodes are added (if you
264// use AddEdge it will add the first argument and then the second).
265template <typename T, bool stable_sort = false,
266 typename Hash = typename absl::flat_hash_map<T, int>::hasher,
267 typename KeyEqual =
268 typename absl::flat_hash_map<T, int, Hash>::key_equal>
270 public:
273
274 // Adds a node to the graph, if it has not already been added via
275 // previous calls to AddNode()/AddEdge(). If no edges are later
276 // added connecting this node, then it remains an isolated node in
277 // the graph. AddNode() only exists to support isolated nodes. There
278 // is no requirement (nor is it an error) to call AddNode() for the
279 // endpoints used in a call to AddEdge(). Dies with a fatal error if
280 // called after a traversal has been started (see TraversalStarted()),
281 // or if more than INT_MAX nodes are being added.
282 void AddNode(const T& node) { int_sorter_.AddNode(LookupOrInsertNode(node)); }
283
284 // Adds a directed edge with the given endpoints to the graph. There
285 // is no requirement (nor is it an error) to call AddNode() for the
286 // endpoints. Dies with a fatal error if called after a traversal
287 // has been started (see TraversalStarted()).
288 void AddEdge(const T& from, const T& to) {
289 // The lookups are not inlined into AddEdge because we need to ensure that
290 // "from" is inserted before "to".
291 const int from_int = LookupOrInsertNode(from);
292 const int to_int = LookupOrInsertNode(to);
293 int_sorter_.AddEdge(from_int, to_int);
294 }
295
296 // Visits the least node in topological order over the current set of
297 // nodes and edges, and marks that node as visited, so that repeated
298 // calls to GetNext() will visit all nodes in order. Writes the newly
299 // visited node in *node and returns true with *cyclic set to false
300 // (assuming the graph has not yet been discovered to be cyclic).
301 // Returns false if all nodes have been visited, or if the graph is
302 // discovered to be cyclic, in which case *cyclic is also set to true.
303 //
304 // If you set the optional argument "output_cycle_nodes" to non-NULL and
305 // a cycle is detected, it will dump an arbitrary cycle of the graph
306 // (whose length will be between 1 and #number_of_nodes, inclusive),
307 // in the natural order: for example if "output_cycle_nodes" is filled
308 // with ["A", "C", "B"], it means that A->C->B->A is a directed cycle
309 // of the graph.
310 //
311 // This starts a traversal (if not started already). Note that the
312 // graph can only be traversed once.
313 bool GetNext(T* node, bool* cyclic_ptr,
314 std::vector<T>* output_cycle_nodes = nullptr) {
316 int node_index;
317 if (!int_sorter_.GetNext(
318 &node_index, cyclic_ptr,
319 output_cycle_nodes ? &cycle_int_nodes_ : nullptr)) {
320 if (*cyclic_ptr && output_cycle_nodes != nullptr) {
321 output_cycle_nodes->clear();
322 for (const int int_node : cycle_int_nodes_) {
323 output_cycle_nodes->push_back(nodes_[int_node]);
324 }
325 }
326 return false;
327 }
328 *node = nodes_[node_index];
329 return true;
330 }
331
332 // Returns the number of nodes that currently have zero indegree.
333 // This starts a traversal (if not started already).
336 return int_sorter_.GetCurrentFringeSize();
337 }
338
339 // Start a traversal. See TraversalStarted(). This initializes the
340 // various data structures of the sorter. Since this takes O(num_nodes
341 // + num_edges) time, users may want to call this at their convenience,
342 // instead of making it happen with the first GetNext().
344 if (TraversalStarted()) return;
345 nodes_.resize(node_to_index_.size());
346 // We move elements from the absl::flat_hash_map to this vector, without
347 // extra copy (if they are movable).
348 for (auto& node_and_index : node_to_index_) {
349 nodes_[node_and_index.second] = std::move(node_and_index.first);
350 }
351 gtl::STLClearHashIfBig(&node_to_index_, 1 << 16);
352 int_sorter_.StartTraversal();
353 }
354
355 // Whether a traversal has started. If true, AddNode() and AddEdge()
356 // can no longer be called.
357 bool TraversalStarted() const { return int_sorter_.TraversalStarted(); }
358
359 private:
360 // A simple mapping from node to their dense index, in 0..num_nodes-1,
361 // which will be their index in nodes_[]. Cleared when a traversal
362 // starts, and replaced by nodes_[].
363 absl::flat_hash_map<T, int, Hash, KeyEqual> node_to_index_;
364
365 // Stores all the nodes as soon as a traversal starts.
366 std::vector<T> nodes_;
367
368 // An internal DenseIntTopologicalSorterTpl that does all the real work.
370
371 // Used internally to extract cycles from the underlying
372 // DenseIntTopologicalSorterTpl.
373 std::vector<int> cycle_int_nodes_;
374
375 // Lookup an existing node's index, or add the node and return the
376 // new index that was assigned to it.
377 int LookupOrInsertNode(const T& node) {
378 return gtl::LookupOrInsert(&node_to_index_, node, node_to_index_.size());
379 }
380
381 DISALLOW_COPY_AND_ASSIGN(TopologicalSorter);
382};
383
384namespace internal {
385// If successful, returns true and outputs the order in "topological_order".
386// If not, returns false and outputs a cycle in "cycle" (if not null).
387template <typename T, typename Sorter>
388ABSL_MUST_USE_RESULT bool RunTopologicalSorter(
389 Sorter* sorter, const std::vector<std::pair<T, T>>& arcs,
390 std::vector<T>* topological_order, std::vector<T>* cycle) {
391 topological_order->clear();
392 for (const auto& arc : arcs) {
393 sorter->AddEdge(arc.first, arc.second);
394 }
395 bool cyclic = false;
396 sorter->StartTraversal();
397 T next;
398 while (sorter->GetNext(&next, &cyclic, cycle)) {
399 topological_order->push_back(next);
400 }
401 return !cyclic;
402}
403
404template <bool stable_sort = false>
405ABSL_MUST_USE_RESULT bool DenseIntTopologicalSortImpl(
406 int num_nodes, const std::vector<std::pair<int, int>>& arcs,
407 std::vector<int>* topological_order) {
409 return RunTopologicalSorter<int, decltype(sorter)>(
410 &sorter, arcs, topological_order, nullptr);
411}
412
413template <typename T, bool stable_sort = false>
414ABSL_MUST_USE_RESULT bool TopologicalSortImpl(
415 const std::vector<T>& nodes, const std::vector<std::pair<T, T>>& arcs,
416 std::vector<T>* topological_order) {
418 for (const T& node : nodes) {
419 sorter.AddNode(node);
420 }
421 return RunTopologicalSorter<T, decltype(sorter)>(&sorter, arcs,
422 topological_order, nullptr);
423}
424
425// Now, the OrDie() versions, which directly return the topological order.
426template <typename T, typename Sorter>
428 Sorter* sorter, const std::vector<std::pair<T, T>>& arcs) {
429 std::vector<T> topo_order;
430 CHECK(RunTopologicalSorter(sorter, arcs, &topo_order, &topo_order))
431 << "Found cycle: " << gtl::LogContainer(topo_order);
432 return topo_order;
433}
434
435template <bool stable_sort = false>
437 int num_nodes, const std::vector<std::pair<int, int>>& arcs) {
439 return RunTopologicalSorterOrDie(&sorter, arcs);
440}
441
442template <typename T, bool stable_sort = false>
444 const std::vector<T>& nodes, const std::vector<std::pair<T, T>>& arcs) {
446 for (const T& node : nodes) {
447 sorter.AddNode(node);
448 }
449 return RunTopologicalSorterOrDie(&sorter, arcs);
450}
451} // namespace internal
452
453// Implementations of the "simple API" functions declared at the top.
455 int num_nodes, const std::vector<std::pair<int, int>>& arcs,
456 std::vector<int>* topological_order) {
457 return internal::DenseIntTopologicalSortImpl<false>(num_nodes, arcs,
458 topological_order);
459}
460
462 int num_nodes, const std::vector<std::pair<int, int>>& arcs,
463 std::vector<int>* topological_order) {
464 return internal::DenseIntTopologicalSortImpl<true>(num_nodes, arcs,
465 topological_order);
466}
467
468template <typename T>
469bool TopologicalSort(const std::vector<T>& nodes,
470 const std::vector<std::pair<T, T>>& arcs,
471 std::vector<T>* topological_order) {
472 return internal::TopologicalSortImpl<T, false>(nodes, arcs,
473 topological_order);
474}
475
476template <typename T>
477bool StableTopologicalSort(const std::vector<T>& nodes,
478 const std::vector<std::pair<T, T>>& arcs,
479 std::vector<T>* topological_order) {
480 return internal::TopologicalSortImpl<T, true>(nodes, arcs, topological_order);
481}
482
483inline std::vector<int> DenseIntTopologicalSortOrDie(
484 int num_nodes, const std::vector<std::pair<int, int>>& arcs) {
485 return internal::DenseIntTopologicalSortOrDieImpl<false>(num_nodes, arcs);
486}
487
489 int num_nodes, const std::vector<std::pair<int, int>>& arcs) {
490 return internal::DenseIntTopologicalSortOrDieImpl<true>(num_nodes, arcs);
491}
492
493template <typename T>
494std::vector<T> TopologicalSortOrDie(const std::vector<T>& nodes,
495 const std::vector<std::pair<T, T>>& arcs) {
496 return internal::TopologicalSortOrDieImpl<T, false>(nodes, arcs);
497}
498
499template <typename T>
501 const std::vector<T>& nodes, const std::vector<std::pair<T, T>>& arcs) {
502 return internal::TopologicalSortOrDieImpl<T, true>(nodes, arcs);
503}
504
505} // namespace util
506
507// BACKWARDS COMPATIBILITY
508// Some of the classes or functions have been exposed under the global namespace
509// or the util::graph:: namespace. Until all clients are fixed to use the
510// util:: namespace, we keep those versions around.
513template <typename T, bool stable_sort = false,
514 typename Hash = typename absl::flat_hash_map<T, int>::hasher,
515 typename KeyEqual =
516 typename absl::flat_hash_map<T, int, Hash>::key_equal>
518 : public ::util::TopologicalSorter<T, stable_sort, Hash, KeyEqual> {};
519
520namespace util {
521namespace graph {
522inline std::vector<int> DenseIntTopologicalSortOrDie(
523 int num_nodes, const std::vector<std::pair<int, int>>& arcs) {
525}
527 int num_nodes, const std::vector<std::pair<int, int>>& arcs) {
529}
530template <typename T>
532 const std::vector<T>& nodes, const std::vector<std::pair<T, T>>& arcs) {
533 return ::util::StableTopologicalSortOrDie<T>(nodes, arcs);
534}
535
536} // namespace graph
537} // namespace util
538
539#endif // UTIL_GRAPH_TOPOLOGICALSORTER_H__
#define CHECK(condition)
Definition: base/logging.h:495
void AddEdge(const T &from, const T &to)
bool GetNext(T *node, bool *cyclic_ptr, std::vector< T > *output_cycle_nodes=nullptr)
void AddNode(const T &node)
void ExtractCycle(std::vector< int > *cycle_nodes) const
static int RemoveDuplicates(std::vector< AdjacencyList > *lists, int skip_lists_smaller_than)
bool GetNext(int *next_node_index, bool *cyclic, std::vector< int > *output_cycle_nodes=nullptr)
Block * next
int arc
auto LogContainer(const ContainerT &container, const PolicyT &policy) -> decltype(gtl::LogRange(container.begin(), container.end(), policy))
Collection::value_type::second_type & LookupOrInsert(Collection *const collection, const typename Collection::value_type::first_type &key, const typename Collection::value_type::second_type &value)
Definition: map_util.h:237
void STLClearHashIfBig(T *obj, size_t limit)
Definition: stl_util.h:180
std::vector< int > DenseIntStableTopologicalSortOrDie(int num_nodes, const std::vector< std::pair< int, int > > &arcs)
std::vector< T > StableTopologicalSortOrDie(const std::vector< T > &nodes, const std::vector< std::pair< T, T > > &arcs)
std::vector< int > DenseIntTopologicalSortOrDie(int num_nodes, const std::vector< std::pair< int, int > > &arcs)
std::vector< T > RunTopologicalSorterOrDie(Sorter *sorter, const std::vector< std::pair< T, T > > &arcs)
std::vector< T > TopologicalSortOrDieImpl(const std::vector< T > &nodes, const std::vector< std::pair< T, T > > &arcs)
ABSL_MUST_USE_RESULT bool DenseIntTopologicalSortImpl(int num_nodes, const std::vector< std::pair< int, int > > &arcs, std::vector< int > *topological_order)
std::vector< int > DenseIntTopologicalSortOrDieImpl(int num_nodes, const std::vector< std::pair< int, int > > &arcs)
ABSL_MUST_USE_RESULT bool RunTopologicalSorter(Sorter *sorter, const std::vector< std::pair< T, T > > &arcs, std::vector< T > *topological_order_or_cycle)
ABSL_MUST_USE_RESULT bool TopologicalSortImpl(const std::vector< T > &nodes, const std::vector< std::pair< T, T > > &arcs, std::vector< T > *topological_order)
uint64_t Hash(uint64_t num, uint64_t c)
Definition: hash.h:150
std::vector< T > TopologicalSortOrDie(const std::vector< T > &nodes, const std::vector< std::pair< T, T > > &arcs)
ABSL_MUST_USE_RESULT bool DenseIntTopologicalSort(int num_nodes, const std::vector< std::pair< int, int > > &arcs, std::vector< int > *topological_order)
ABSL_MUST_USE_RESULT bool TopologicalSort(const std::vector< T > &nodes, const std::vector< std::pair< T, T > > &arcs, std::vector< T > *topological_order)
std::vector< int > DenseIntStableTopologicalSortOrDie(int num_nodes, const std::vector< std::pair< int, int > > &arcs)
::util::internal::DenseIntTopologicalSorterTpl< true > DenseIntStableTopologicalSorter
std::vector< int > FindCycleInDenseIntGraph(int num_nodes, const std::vector< std::pair< int, int > > &arcs)
std::vector< int > DenseIntTopologicalSortOrDie(int num_nodes, const std::vector< std::pair< int, int > > &arcs)
ABSL_MUST_USE_RESULT bool StableTopologicalSort(const std::vector< T > &nodes, const std::vector< std::pair< T, T > > &arcs, std::vector< T > *topological_order)
std::vector< T > StableTopologicalSortOrDie(const std::vector< T > &nodes, const std::vector< std::pair< T, T > > &arcs)
::util::internal::DenseIntTopologicalSorterTpl< false > DenseIntTopologicalSorter
ABSL_MUST_USE_RESULT bool DenseIntStableTopologicalSort(int num_nodes, const std::vector< std::pair< int, int > > &arcs, std::vector< int > *topological_order)
int nodes
::util::DenseIntStableTopologicalSorter DenseIntStableTopologicalSorter
::util::DenseIntTopologicalSorter DenseIntTopologicalSorter