Aleph-w  1.9
General library for algorithms and data structures
Tarjan.H
1 
2 /* Aleph-w
3 
4  / \ | | ___ _ __ | |__ __ __
5  / _ \ | |/ _ \ '_ \| '_ \ ____\ \ /\ / / Data structures & Algorithms
6  / ___ \| | __/ |_) | | | |_____\ V V / version 1.9b
7  /_/ \_\_|\___| .__/|_| |_| \_/\_/ https://github.com/lrleon/Aleph-w
8  |_|
9 
10  This file is part of Aleph-w library
11 
12  Copyright (c) 2002-2018 Leandro Rabindranath Leon & Alejandro Mujica
13 
14  This program is free software: you can redistribute it and/or modify
15  it under the terms of the GNU General Public License as published by
16  the Free Software Foundation, either version 3 of the License, or
17  (at your option) any later version.
18 
19  This program is distributed in the hope that it will be useful, but
20  WITHOUT ANY WARRANTY; without even the implied warranty of
21  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22  General Public License for more details.
23 
24  You should have received a copy of the GNU General Public License
25  along with this program. If not, see <https://www.gnu.org/licenses/>.
26 */
27 # ifndef TARJAN_H
28 # define TARJAN_H
29 
30 # include <tpl_dynListStack.H>
31 # include <tpl_dynSetTree.H>
32 # include <htlist.H>
33 # include <tpl_graph_utils.H>
34 # include <tpl_find_path.H>
35 
36 namespace Aleph {
37 
38 
49  template <class GT,
50  template <typename, class> class Itor = Out_Iterator,
51  class SA = Dft_Show_Arc<GT>>
53 {
54  SA sa;
55 
56  GT * g_ptr = nullptr;
57 
59 
60  long df_count = 0;
61  mutable size_t n = 0; // número de nodos del grafo
62 
63  // lista de listas; cada lista almacena los nodos de los bloques
64  DynList<DynList<typename GT::Node*>> * list_list_ptr = nullptr;
65 
66  DynList <GT> * block_list_ptr = nullptr; // lista de bloques fuertemente conexos
67 
68  DynList<size_t> * list_len_ptr = nullptr; // lista de tamaños de componentes
69 
70  Path<GT> * path_ptr = nullptr;
71 
72 public:
73 
74  Tarjan_Connected_Components(SA __sa = SA())
75  noexcept(std::is_nothrow_move_assignable<SA>::value)
76  : sa(__sa) { /* empty */ }
77 
78 private:
79 
80  struct Init_Tarjan_Node
81  {
82  void operator () (const GT & g, typename GT::Node * p) const noexcept
83  {
84  g.reset_bits(p);
85  g.reset_counter(p); // inicializa df
86  low <GT> (p) = -1; // inicializa low
87  }
88  };
89 
90  bool is_node_in_stack(typename GT::Node * p) const noexcept
91  {
92  return IS_NODE_VISITED(p, Aleph::Min);
93  }
94 
95  void init_node_and_push_in_stack(typename GT::Node * p)
96  {
97  assert(not is_node_in_stack(p));
98 
99  stack.push(p);
100  NODE_BITS(p).set_bit(Aleph::Min, true);
101  NODE_BITS(p).set_bit(Aleph::Depth_First, true);
102  df<GT>(p) = low<GT>(p) = df_count++;
103  }
104 
105  typename GT::Node * pop_from_stack()
106  {
107  auto ret = stack.pop();
108  NODE_BITS(ret).set_bit(Aleph::Min, false);
109 
110  return ret;
111  }
112 
113  void scc_by_blocks(typename GT::Node * v)
114  {
115  init_node_and_push_in_stack(v);
116 
117  // recorrer en profundidad todos los nodos conectados a v
118  for (Itor<GT, SA> it(v, sa); it.has_curr(); it.next_ne())
119  {
120  auto w = g_ptr->get_tgt_node(it.get_curr_ne());
121  if (not IS_NODE_VISITED(w, Aleph::Depth_First))
122  {
123  scc_by_blocks(w);
124  low <GT> (v) = std::min(low <GT> (v), low <GT> (w));
125  }
126  else if (is_node_in_stack(w))
127  // si está en pila ==> v fue visitado antes que p
128  low <GT> (v) = std::min(low <GT> (v), df <GT> (w));
129  }
130 
131  if (low <GT> (v) == df <GT> (v)) // ¿primer nodo visitado del bloque?
132  { // sí ==> saque los nodos del bloque que están en pila
133  const auto & blk_idx = block_list_ptr->size();
134  GT & blk = block_list_ptr->append(GT());
135 
136  while (true) // sacar el bloque de la pila hasta sacar a v
137  {
138  auto p = pop_from_stack();
139  auto q = blk.insert_node(p->get_info());
140  *q = *q; // copia contenido del nodo
141  NODE_COOKIE(p) = NODE_COOKIE(q) = nullptr;
142  GT::map_nodes(p, q);
143  NODE_COUNTER(p) = NODE_COUNTER(q) = blk_idx;
144  if (p == v)
145  break;
146  }
147  }
148  }
149 
150  void scc_by_lists(typename GT::Node * v)
151  {
152  init_node_and_push_in_stack(v);
153 
154  // recorrer en profundidad todos los nodos conectados a v
155  for (Itor<GT, SA> it(v, sa); it.has_curr(); it.next_ne())
156  {
157  auto w = g_ptr->get_tgt_node(it.get_curr_ne());
158  if (not IS_NODE_VISITED(w, Aleph::Depth_First))
159  {
160  scc_by_lists(w);
161  low <GT> (v) = std::min(low <GT> (v), low <GT> (w));
162  }
163  else if (is_node_in_stack(w))
164  // si está en pila ==> v fue visitado antes que p
165  low <GT> (v) = std::min(low <GT> (v), df <GT> (w));
166  }
167 
168  if (low<GT>(v) == df<GT>(v)) // ¿primer nodo visitado del bloque?
169  { // sí ==> saque los nodos del bloque que están en pila
170  auto & l = list_list_ptr->append(DynList<typename GT::Node*>());
171  while (true) // sacar bloque de pila hasta llegar a v
172  {
173  auto p = pop_from_stack();
174  l.append(p);
175  if (p == v)
176  break;
177  }
178  }
179  }
180 
181  void scc_by_len(typename GT::Node * v)
182  {
183  init_node_and_push_in_stack(v);
184 
185  // recorrer en profundidad todos los nodos conectados a v
186  for (Itor<GT, SA> it(v, sa); it.has_curr(); it.next_ne())
187  {
188  auto w = g_ptr->get_tgt_node(it.get_curr_ne());
189  if (not IS_NODE_VISITED(w, Aleph::Depth_First))
190  {
191  scc_by_len(w);
192  low <GT> (v) = std::min(low <GT> (v), low <GT> (w));
193  }
194  else if (is_node_in_stack(w))
195  // si está en pila ==> v fue visitado antes que p
196  low <GT> (v) = std::min(low <GT> (v), df <GT> (w));
197  }
198 
199  if (low<GT>(v) == df<GT>(v)) // ¿primer nodo visitado del bloque?
200  { // sí ==> saque los nodos del bloque que están en pila
201  size_t count = 0;
202  while (true) // sacar bloque de pila hasta llegar a v
203  {
204  auto p = pop_from_stack();
205  ++count;
206 
207  if (p == v)
208  break;
209  }
210  list_len_ptr->append(count);
211  }
212  }
213 
214  void init_tarjan(const GT & g)
215  {
216  Operate_On_Nodes <GT, Init_Tarjan_Node> () (g); // inicia bits, df y low
217  df_count = 0; // contador de visitas
218  stack.empty();
219  n = g.get_num_nodes();
220  g_ptr = &const_cast<GT&>(g);
221  }
222 
223  // retorna true si se encuentra un ciclo desde v
224  bool has_cycle(typename GT::Node * v)
225  {
226  init_node_and_push_in_stack(v);
227 
228  // recorrer en profundidad todos los nodos conectados a v
229  for (Itor <GT, SA> it(v, sa); it.has_curr(); it.next_ne())
230  {
231  auto w = g_ptr->get_tgt_node(it.get_curr_ne());
232  if (not IS_NODE_VISITED(w, Aleph::Depth_First))
233  {
234  if (has_cycle(w))
235  return true;
236 
237  low<GT>(v) = std::min(low<GT>(v), low<GT>(w));
238  }
239  else if (is_node_in_stack(w))
240  // si está en pila ==> v fue visitado antes que p
241  low<GT>(v) = std::min(low<GT>(v), df<GT>(w));
242  }
243 
244  if (low<GT>(v) == df<GT>(v)) // ¿primer nodo visitado de bloque?
245  { // sí ==> verifique si tiene dos o más nodos
246  int i = 0;
247  for (; i < 2; ++i) // sacar bloque de la pila hasta sacar a v
248  if (pop_from_stack() == v)
249  break;
250 
251  return i >= 2; // si i>= 2 ==> hay un ciclo
252  }
253 
254  return false; // se recorrió todo v sin encontrar ciclo
255  }
256 
257  // Toma el bloque block, que está mapeado del grafo original, que es
258  // fuertemente conexo, y construye en path_ptr el ciclo
259  void
260  build_path(const GT & block,
262  {
263  // buscar ciclo en blk.
264  auto a = block.get_first_arc();
265  auto start = block.get_tgt_node(a);
266  auto end = block.get_src_node(a);
267  assert(start != end);
268 
269  auto aux_path = Directed_Find_Path<GT, Itor, SA>(block, sa).dfs(start, end);
270  assert(not aux_path.is_empty()); // como es conexo debe encontrarse
271 
272  // aux_path es sobre el bloque mapeado. Pero nosotros necesitamos
273  // que el camino sea sobre el grafo original. Los nodos del grafo
274  // original se recuperan del mapeo table
275  path_ptr->empty();
276  for (typename Path<GT>::Iterator i(aux_path); i.has_curr(); i.next_ne())
277  path_ptr->append_directed(table.find(i.get_current_node_ne()));
278 
279  path_ptr->append_directed(path_ptr->get_first_node());
280  }
281 
282  // retorna true si encuentra un ciclo, en cuyo caso lo pone en
283  // path. De lo contrario, retorna false y path queda intacto
284  bool build_cycle(typename GT::Node * v)
285  {
286  init_node_and_push_in_stack(v);
287 
288  // recorrer en profundidad todos los nodos conectados a v
289  for (Itor <GT, SA> it(v, sa); it.has_curr(); it.next_ne())
290  {
291  auto w = g_ptr->get_tgt_node(it.get_curr_ne());
292  if (not IS_NODE_VISITED(w, Aleph::Depth_First))
293  {
294  if (build_cycle(w))
295  return true;
296 
297  low<GT>(v) = std::min(low<GT>(v), low<GT>(w));
298  }
299  else if (is_node_in_stack(w))
300  // si está en pila ==> v fue visitado antes que p
301  low<GT>(v) = std::min(low<GT>(v), df<GT>(w));
302  }
303 
304  if (low<GT>(v) == df<GT>(v)) // ¿primer nodo visitado del bloque?
305  {
306  GT blk; // grafo auxiliar
307 
308  // mapeo de nodos de g con blk (los cookies están ocupados)
310 
311  // saque nodos de pila e insertelos en bloque auxiliar
312  while (true) // saca el componente y lo inserta en blk
313  {
314  auto p = pop_from_stack();
315  auto q = blk.insert_node();
316  *q = *p; // copia contenido del nodo
317  table.insert(q, p);
318  table.insert(p, q);
319  if (p == v)
320  break;
321  }
322 
323  if (blk.get_num_nodes() == 1)
324  return false; // si blk sólo tiene un nodo no hay ciclo
325 
326  // terminanos de construir el bloque con los arcos
327  for (typename GT::Node_Iterator j(blk); j.has_curr(); j.next_ne())
328  {
329  auto bsrc = j.get_curr_ne();
330  auto gsrc = table.find(bsrc);
331 
332  // recorrer los arcos de gsrc
333  for (Itor <GT, SA> k(gsrc, sa); k.has_curr(); k.next_ne())
334  {
335  auto ga = k.get_curr_ne();
336  auto gtgt = g_ptr->get_tgt_node(ga);
337  auto ptr = table.search(gtgt);
338  if (ptr == nullptr) // ¿arco del bloque?
339  continue;
340 
341  auto ta = blk.insert_arc(bsrc, ptr->second);
342  *ta = *ga; // copia contenido del arco
343  }
344  }
345 
346  build_path(blk, table);
347 
348  return true;
349  }
350 
351  assert(path_ptr->is_empty());
352 
353  return false;
354  }
355 
356  bool is_connected(typename GT::Node * v)
357  {
358  init_node_and_push_in_stack(v);
359 
360  // recorrer en profundidad todos los nodos conectados a v
361  for (Itor <GT, SA> it(v, sa); it.has_curr(); it.next_ne())
362  {
363  auto w = g_ptr->get_tgt_node(it.get_curr_ne());
364  if (not IS_NODE_VISITED(w, Aleph::Depth_First))
365  {
366  if (not is_connected(w))
367  return false;
368 
369  low <GT> (v) = std::min(low <GT> (v), low <GT> (w));
370  }
371  else if (is_node_in_stack(w))
372  low <GT> (v) = std::min(low <GT> (v), df <GT> (w));
373  }
374 
375  if (low<GT>(v) == df<GT>(v)) // ¿primer nodo visitado de bloque?
376  { // saque nodos de pila hasta encontrar a v
377  while (pop_from_stack() != v);
378 
379  return stack.is_empty();
380  }
381 
382  return true;
383  }
384 
385 public:
386 
415  void connected_components(const GT & g, DynList<GT> & blk_list,
416  DynList<typename GT::Arc*> & arc_list)
417  {
418  init_tarjan(g);
419 
420  block_list_ptr = &blk_list;
421 
422  for (typename GT::Node_Iterator it(g); df_count < n; it.next_ne())
423  {
424  auto v = it.get_curr_ne();
425  if (not IS_NODE_VISITED(v, Aleph::Depth_First))
426  scc_by_blocks(v);
427  }
428 
429  assert(stack.is_empty());
430 
431  // recorrer cada uno de subgrafos parciales y añadir sus arcos
432  for (typename DynList<GT>::Iterator i(blk_list); i.has_curr(); i.next_ne())
433  { // recorrer todos los nodos del bloque
434  GT & blk = i.get_curr_ne();
435  for (typename GT::Node_Iterator j(blk); j.has_curr(); j.next_ne())
436  {
437  auto bsrc = j.get_curr_ne();
438  auto gsrc = mapped_node<GT>(bsrc);
439 
440  // recorrer arcos de gsrc
441  for (Itor<GT, SA> k(gsrc, sa); k.has_curr(); k.next_ne())
442  {
443  auto ga = k.get_curr_ne();
444  auto gtgt = g_ptr->get_tgt_node(ga);
445  if (NODE_COUNTER(gsrc) != NODE_COUNTER(gtgt))
446  { // arco inter-bloque ==> añádalo a arc_list
447  arc_list.append(ga);
448  continue;
449  }
450 
451  // insertar y mapear el arco en el sub-bloque
452  auto btgt = mapped_node<GT>(gtgt);
453  auto ba = blk.insert_arc(bsrc, btgt);
454  *ba = *ga; // copia contenido del arco
455  GT::map_arcs(ga, ba);
456  }
457  }
458  }
459  }
460 
494  void connected_components(const GT & g,
496  {
497  init_tarjan(g);
498  list_list_ptr = &blks;
499  for (typename GT::Node_Iterator it(g); df_count < n; it.next_ne())
500  {
501  auto v = it.get_curr_ne();
502  if (not IS_NODE_VISITED(v, Aleph::Depth_First))
503  scc_by_lists(v);
504  }
505  }
506 
508  {
510  connected_components(g, blks);
511  return blks;
512  }
513 
536  void connected_components(const GT & g, DynList<size_t> & blks)
537  {
538  init_tarjan(g);
539  list_len_ptr = &blks;
540  for (typename GT::Node_Iterator it(g); df_count < n; it.next_ne())
541  {
542  auto v = it.get_curr_ne();
543  if (not IS_NODE_VISITED(v, Aleph::Depth_First))
544  scc_by_len(v);
545  }
546  }
547 
549  void operator () (const GT & g,
550  DynList <GT> & blk_list,
551  DynList<typename GT::Arc*> & arc_list)
552  {
553  connected_components(g, blk_list, arc_list);
554  }
555 
557  void operator () (const GT & g,
559  {
560  connected_components(g, blks);
561  }
562 
564  {
565  return connected_components(g);
566  }
567 
569  void operator () (const GT & g,
570  DynDlist <GT> & blk_list,
571  DynDlist<typename GT::Arc*> & arc_list)
572  {
573  DynList<GT> blist;
575  connected_components(g, blist, alist);
576 
577  for (typename DynList<GT>::Iterator it(blist); it.has_curr(); it.next_ne())
578  {
579  GT & curr = it.get_curr_ne();
580  GT & block = blk_list.append(GT());
581  curr.swap(block);
582  }
583 
584  for (typename DynList<typename GT::Arc*>::Iterator it(alist);
585  it.has_curr(); it.next_ne())
586  arc_list.append(it.get_curr_ne());
587  }
588 
590  void operator () (const GT & g,
592  {
594  connected_components(g, b);
595 
596  for (typename DynList<DynList<typename GT::Node*>>::Iterator it(b);
597  it.has_curr(); it.next_ne())
598  {
599  auto & tgt_list = blks.append(DynDlist<typename GT::Node*>());
600 
601  auto & blk = it.get_curr_ne();
602  while (not blk.is_empty())
603  tgt_list.append(blk.remove_first());
604  }
605  }
606 
608  bool has_cycle(const GT & g)
609  {
610  init_tarjan(g);
611  for (typename GT::Node_Iterator it(g); df_count < n; it.next_ne())
612  {
613  auto v = it.get_curr_ne();
614  if (not IS_NODE_VISITED(v, Aleph::Depth_First))
615  if (has_cycle(v))
616  return true;
617  }
618 
619  return false;
620  }
621 
623  bool is_dag(const GT & g)
624  {
625  return not has_cycle(g);
626  }
627 
628  bool compute_cycle(const GT & g, Path<GT> & path)
629  {
630  init_tarjan(g);
631  path_ptr = &path;
632  path_ptr->set_graph(g);
633 
634  for (typename GT::Node_Iterator it(g); df_count < n; it.next_ne())
635  {
636  auto v = it.get_curr_ne();
637  if (not IS_NODE_VISITED(v, Aleph::Depth_First)) // ¿p visitado?
638  if (build_cycle(v))
639  return true;
640  }
641 
642  path.empty();
643  return false;
644  }
645 
646  bool compute_cycle(const GT & g, typename GT::Node * src, Path<GT> & path)
647  {
648  init_tarjan(g);
649  path_ptr = &path;
650  path_ptr->set_graph(g);
651  return build_cycle(src);
652  }
653 
654  bool test_connectivity(const GT & g)
655  {
656  init_tarjan(g);
657  for (typename GT::Node_Iterator it(g); df_count < n; it.next_ne())
658  {
659  auto v = it.get_curr_ne();
660  if (not IS_NODE_VISITED(v, Aleph::Depth_First))
661  if (not is_connected(v))
662  return false;
663  }
664 
665  assert(stack.is_empty());
666 
667  return true;
668  }
669 };
670 
671 
672 
691  template <class GT,
692  template <typename, class> class Itor = Out_Iterator,
693  class SA = Dft_Show_Arc<GT>>
695 {
696  SA & sa;
697 
698 public:
699 
700  Compute_Cycle_In_Digraph(SA && __sa = SA())
701  noexcept(std::is_nothrow_move_assignable<SA>::value)
702  : sa(__sa) { /* empty */ }
703 
704  Compute_Cycle_In_Digraph(SA & __sa)
705  noexcept(std::is_nothrow_copy_assignable<SA>::value)
706  : sa(__sa) { /* empty */ }
707 
716  bool operator () (const GT & g, Path<GT> & path) const
717  {
719 
720  return tarjan.compute_cycle(g, path);
721  }
722 
723  Path<GT> operator () (const GT & g) const
724  {
725  Path<GT> ret(g);
726  Tarjan_Connected_Components<GT, Itor, SA> (sa).compute_cycle(g, ret);
727  return ret;
728  }
729 
730  Path<GT> operator () (const GT & g, typename GT::Node * src) const
731  {
732  Path<GT> ret(g);
733  Tarjan_Connected_Components<GT, Itor, SA>(sa).compute_cycle(g, src, ret);
734  return ret;
735  }
736 };
737 
738 } // end namespace Aleph
739 
740 # endif // TARJAN_H
size_t size() const noexcept
Definition: htlist.H:1240
void empty() noexcept
empty the list
Definition: htlist.H:1598
Node * get_first_node() const
Definition: tpl_graph.H:3069
Pair * insert(const Key &key, const Type &data)
Definition: tpl_dynMapTree.H:112
#define NODE_COOKIE(p)
Definition: aleph-graph.H:333
Definition: Stack.H:45
Definition: tpl_dynMapTree.H:328
bool test_connectivity(const GT &g)
Definition: tpl_graph_utils.H:565
Definition: tpl_dynDlist.H:51
Definition: tpl_graph.H:2493
#define IS_NODE_VISITED(p, bit)
Definition: aleph-graph.H:327
T & push(const T &item)
Definition: htlist.H:1432
bool is_empty() const noexcept
Definition: htlist.H:466
bool has_curr() const noexcept
Definition: htlist.H:1071
#define NODE_COUNTER(p)
Definition: aleph-graph.H:311
void map_arcs(typename GTS::Arc *p, typename GTT::Arc *q) noexcept
Definition: tpl_graph.H:3445
bool has_cycle(const GT &g)
Retorna true si el digrafo g contiene al menos un ciclo.
Definition: Tarjan.H:608
Definition: tpl_graph.H:53
void connected_components(const GT &g, DynList< DynList< typename GT::Node *>> &blks)
Definition: Tarjan.H:494
void map_nodes(typename GTS::Node *p, typename GTT::Node *q) noexcept
Definition: tpl_graph.H:3419
Definition: ah-comb.H:35
Definition: tpl_find_path.H:411
Definition: tpl_graph.H:1063
void append_directed(Node *p)
Definition: tpl_graph.H:2880
bool is_dag(const GT &g)
Retorna true si el grafo dirigido es acíclico.
Definition: Tarjan.H:623
Pair * search(const Key &key) const noexcept
Definition: tpl_dynMapTree.H:214
Definition: tpl_graph.H:1796
#define NODE_BITS(p)
Definition: aleph-graph.H:305
void connected_components(const GT &g, DynList< size_t > &blks)
Definition: Tarjan.H:536
T pop()
Definition: htlist.H:1543
void set_graph(const GT &__g, Node *start_node=nullptr)
Definition: tpl_graph.H:2745
void connected_components(const GT &g, DynList< GT > &blk_list, DynList< typename GT::Arc *> &arc_list)
Definition: Tarjan.H:415
bool is_empty() const noexcept
Return true if the path is empty.
Definition: tpl_graph.H:2758
Definition: htlist.H:1622
void operator()(const GT &g, DynList< GT > &blk_list, DynList< typename GT::Arc *> &arc_list)
Definition: Tarjan.H:549
Definition: ahDry.H:41
void empty()
Clean the path: all the nodes and arc are removed.
Definition: tpl_graph.H:2761
T & append(const T &item)
Definition: htlist.H:1471
Definition: Tarjan.H:694
Definition: tpl_graph.H:3135
T & append(const T &item)
Definition: tpl_dynDlist.H:149
Definition: Tarjan.H:52
Type & find(const Key &key)
Definition: tpl_dynMapTree.H:242

Leandro Rabindranath León