Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
graph_to_tree_example.cc
Go to the documentation of this file.
1
25#include <iostream>
26#include <tpl_graph.H>
27#include <tpl_tree_node.H>
28#include <graph_to_tree.H>
29#include <tpl_graph_utils.H>
30#include <generate_tree.H>
31
32using namespace std;
33using namespace Aleph;
34
35// =============================================================================
36// Helper: Print Tree_Node structure recursively
37// =============================================================================
38template <typename T>
39void print_tree(Tree_Node<T>* node, int depth = 0)
40{
41 if (node == nullptr)
42 return;
43
44 for (int i = 0; i < depth; ++i)
45 cout << " ";
46
47 cout << node->get_key() << endl;
48
49 auto* child = node->get_left_child();
50 while (child != nullptr)
51 {
52 print_tree(child, depth + 1);
53 child = child->get_right_sibling();
54 }
55}
56
57// =============================================================================
58// Helper: Delete Tree_Node structure recursively
59// =============================================================================
60template <typename T>
62{
63 if (node == nullptr)
64 return;
65
66 auto* child = node->get_left_child();
67 while (child != nullptr)
68 {
69 auto* next = child->get_right_sibling();
70 delete_tree(child);
71 child = next;
72 }
73
74 delete node;
75}
76
77int main()
78{
79 cout << "=== Graph to Tree Conversion: Educational Examples ===\n\n";
80
81 // =========================================================================
82 // EXAMPLE 1: Convert an Acyclic Graph (Tree) Directly
83 // =========================================================================
84 {
85 cout << "--- Example 1: Converting an Acyclic Graph (Tree) ---\n\n";
86
88 GT g;
89
90 cout << "Building a tree-structured graph:\n";
91 cout << " A <- root\n";
92 cout << " / \\\n";
93 cout << " B C\n";
94 cout << " / \\ \\\n";
95 cout << " D E F\n\n";
96
97 auto a = g.insert_node("A");
98 auto b = g.insert_node("B");
99 auto c = g.insert_node("C");
100 auto d = g.insert_node("D");
101 auto e = g.insert_node("E");
102 auto f = g.insert_node("F");
103
104 // Tree edges only - no cycles!
105 g.insert_arc(a, b);
106 g.insert_arc(a, c);
107 g.insert_arc(b, d);
108 g.insert_arc(b, e);
109 g.insert_arc(c, f);
110
111 cout << "Graph has " << g.get_num_nodes() << " nodes and "
112 << g.get_num_arcs() << " arcs\n";
113 cout << "This graph is acyclic (a tree with n-1 = 5 edges)\n\n";
114
115 // Converter functor
116 struct CopyString {
117 void operator()(GT::Node* gnode, Tree_Node<string>* tnode) {
118 tnode->get_key() = gnode->get_info();
119 }
120 };
121
122 cout << "Converting to Tree_Node structure...\n";
123
124 // Use the functor class to avoid ambiguity
126 auto* tree_root = converter(g, a);
127
128 cout << "Result (Tree_Node hierarchy):\n";
129 print_tree(tree_root);
130
131 cout << "\nKEY POINT: graph_to_tree_node() only works on acyclic graphs!\n";
132 cout << " If your graph has cycles, it will throw domain_error.\n\n";
133
134 delete_tree(tree_root);
135 }
136
137 // =========================================================================
138 // EXAMPLE 2: Graph with Cycles -> Spanning Tree -> Tree_Node
139 // =========================================================================
140 {
141 cout << "--- Example 2: Graph with Cycles (Two-Step Process) ---\n\n";
142
144 GT g;
145
146 cout << "Building a graph WITH cycles:\n";
147 cout << " A\n";
148 cout << " / \\\n";
149 cout << " B---C <- B-C creates a cycle A-B-C-A\n";
150 cout << " | |\n";
151 cout << " D---E <- D-E creates another cycle\n\n";
152
153 auto a = g.insert_node("A");
154 auto b = g.insert_node("B");
155 auto c = g.insert_node("C");
156 auto d = g.insert_node("D");
157 auto e = g.insert_node("E");
158
159 g.insert_arc(a, b);
160 g.insert_arc(a, c);
161 g.insert_arc(b, c); // Creates cycle!
162 g.insert_arc(b, d);
163 g.insert_arc(c, e);
164 g.insert_arc(d, e); // Creates another cycle!
165
166 cout << "Graph has " << g.get_num_nodes() << " nodes and "
167 << g.get_num_arcs() << " arcs\n";
168 cout << "A tree would have only " << g.get_num_nodes() - 1 << " edges.\n";
169 cout << "This graph has cycles (6 edges > 4 needed for tree)\n\n";
170
171 // Step 1: Extract a spanning tree using DFS (returns a new graph)
172 cout << "Step 1: Extract DFS spanning tree...\n";
173 GT spanning_tree = find_depth_first_spanning_tree<GT>(g, a);
174
175 cout << "Spanning tree has " << spanning_tree.get_num_nodes() << " nodes and "
176 << spanning_tree.get_num_arcs() << " arcs\n";
177 cout << "(Exactly n-1 = 4 edges, as expected for a tree)\n\n";
178
179 // Step 2: Find the root in the spanning tree
180 GT::Node* st_root = nullptr;
181 for (GT::Node_Iterator it(spanning_tree); it.has_curr(); it.next())
182 {
183 if (it.get_curr()->get_info() == "A")
184 {
185 st_root = it.get_curr();
186 break;
187 }
188 }
189
190 // Step 3: Convert spanning tree to Tree_Node
191 cout << "Step 2: Convert spanning tree to Tree_Node...\n";
192
193 struct CopyString {
194 void operator()(GT::Node* gnode, Tree_Node<string>* tnode) {
195 tnode->get_key() = gnode->get_info();
196 }
197 };
198
200 auto* tree_root = converter(spanning_tree, st_root);
201
202 cout << "Result (Tree_Node hierarchy):\n";
203 print_tree(tree_root);
204
205 cout << "\nNOTE: The spanning tree removed the cycle-creating edges.\n\n";
206
207 delete_tree(tree_root);
208 }
209
210 // =========================================================================
211 // EXAMPLE 3: BFS vs DFS Spanning Trees
212 // =========================================================================
213 {
214 cout << "--- Example 3: BFS vs DFS Spanning Trees ---\n\n";
215
217 GT g;
218
219 cout << "Building a grid graph:\n";
220 cout << " 0---1---2\n";
221 cout << " | | |\n";
222 cout << " 3---4---5\n\n";
223
224 // Insert nodes and keep track of them
225 GT::Node* n[6];
226 for (int i = 0; i < 6; ++i)
227 n[i] = g.insert_node(i);
228
229 // Horizontal edges
230 g.insert_arc(n[0], n[1]);
231 g.insert_arc(n[1], n[2]);
232 g.insert_arc(n[3], n[4]);
233 g.insert_arc(n[4], n[5]);
234 // Vertical edges
235 g.insert_arc(n[0], n[3]);
236 g.insert_arc(n[1], n[4]);
237 g.insert_arc(n[2], n[5]);
238
239 struct CopyInt {
240 void operator()(GT::Node* gnode, Tree_Node<int>* tnode) {
241 tnode->get_key() = gnode->get_info();
242 }
243 };
244
245 // DFS spanning tree
246 {
248
249 GT::Node* root = nullptr;
250 for (GT::Node_Iterator it(dfs_tree); it.has_curr(); it.next())
251 if (it.get_curr()->get_info() == 0)
252 {
253 root = it.get_curr();
254 break;
255 }
256
258 auto* tree_root = converter(dfs_tree, root);
259
260 cout << "DFS Spanning Tree (tends to be DEEP):\n";
261 print_tree(tree_root);
262 cout << endl;
263
264 delete_tree(tree_root);
265 }
266
267 // BFS spanning tree
268 {
270
271 GT::Node* root = nullptr;
272 for (GT::Node_Iterator it(bfs_tree); it.has_curr(); it.next())
273 if (it.get_curr()->get_info() == 0)
274 {
275 root = it.get_curr();
276 break;
277 }
278
280 auto* tree_root = converter(bfs_tree, root);
281
282 cout << "BFS Spanning Tree (tends to be SHALLOW):\n";
283 print_tree(tree_root);
284 cout << endl;
285
286 delete_tree(tree_root);
287 }
288
289 cout << "DFS: Explores one path deeply before backtracking.\n";
290 cout << "BFS: Explores all neighbors at distance k before k+1.\n";
291 cout << " BFS tree gives shortest paths from root!\n\n";
292 }
293
294 // =========================================================================
295 // EXAMPLE 4: Using Graph_To_Tree_Node Functor Class
296 // =========================================================================
297 {
298 cout << "--- Example 4: Functor Class API ---\n\n";
299
301 GT g;
302
303 auto a = g.insert_node('A');
304 auto b = g.insert_node('B');
305 auto c = g.insert_node('C');
306
307 g.insert_arc(a, b);
308 g.insert_arc(a, c);
309
310 cout << "Simple tree: A -> {B, C}\n\n";
311
312 struct CopyChar {
313 void operator()(GT::Node* gnode, Tree_Node<char>* tnode) {
314 tnode->get_key() = gnode->get_info();
315 }
316 };
317
319 auto* tree_root = converter(g, a);
320
321 cout << "Converted using Graph_To_Tree_Node functor:\n";
322 print_tree(tree_root);
323
324 cout << "\nThe functor class allows storing arc filters.\n\n";
325
326 delete_tree(tree_root);
327 }
328
329 // =========================================================================
330 // EXAMPLE 5: Generating Tree Specification for ntreepic
331 // =========================================================================
332 {
333 cout << "--- Example 5: Generate ntreepic Specification ---\n\n";
334
336 GT g;
337
338 auto root = g.insert_node("Root");
339 auto l1 = g.insert_node("L1");
340 auto l2 = g.insert_node("L2");
341 auto l1a = g.insert_node("L1a");
342 auto l1b = g.insert_node("L1b");
343
344 g.insert_arc(root, l1);
345 g.insert_arc(root, l2);
346 g.insert_arc(l1, l1a);
347 g.insert_arc(l1, l1b);
348
349 struct CopyString {
350 void operator()(GT::Node* gnode, Tree_Node<string>* tnode) {
351 tnode->get_key() = gnode->get_info();
352 }
353 };
354
356 auto* tree_root = converter(g, root);
357
358 cout << "Tree structure:\n";
359 print_tree(tree_root);
360
361 cout << "\nntreepic specification (for visualization):\n";
362 cout << "-------------------------------------------\n";
364 cout << "-------------------------------------------\n";
365 cout << "\nThis output can be used with ntreepic to generate LaTeX.\n\n";
366
367 delete_tree(tree_root);
368 }
369
370 cout << "=== SUMMARY ===\n\n";
371 cout << "1. Graph_To_Tree_Node: Converts ACYCLIC graphs only\n";
372 cout << " - Input must be a tree (no cycles)\n";
373 cout << " - Throws domain_error if cycles detected\n\n";
374 cout << "2. For graphs WITH cycles:\n";
375 cout << " a) Extract spanning tree: find_depth_first_spanning_tree()\n";
376 cout << " or find_breadth_first_spanning_tree()\n";
377 cout << " b) Convert spanning tree: Graph_To_Tree_Node()(tree, root)\n\n";
378 cout << "3. DFS vs BFS spanning trees:\n";
379 cout << " - DFS: Deep, narrow trees\n";
380 cout << " - BFS: Shallow, wide trees (shortest paths from root)\n\n";
381 cout << "4. Tree_Node can be visualized with generate_tree() + ntreepic\n\n";
382
383 return 0;
384}
Functor class to convert a tree graph to Tree_Node structure.
virtual Node * insert_node(Node *node) noexcept
Insertion of a node already allocated.
Definition tpl_graph.H:524
Arc * insert_arc(Node *src_node, Node *tgt_node, void *a)
Definition tpl_graph.H:604
Generic m-ary trees.
Tree_Node * get_left_child() const noexcept
Returns the leftmost child of this.
Tree_Node * get_right_sibling() const noexcept
Returns the right sibling of this.
T & get_key() noexcept
Returns a modifiable reference to the node contents.
constexpr size_t get_num_nodes() const noexcept
Return the total of nodes of graph.
Definition graph-dry.H:695
constexpr size_t get_num_arcs() const noexcept
Definition graph-dry.H:778
Tree visualization and output generation.
__gmp_expr< T, __gmp_binary_expr< __gmp_expr< T, U >, unsigned long int, __gmp_root_function > > root(const __gmp_expr< T, U > &expr, unsigned long int l)
Definition gmpfrxx.h:4060
Convert spanning tree graphs to Tree_Node structures.
void delete_tree(Tree_Node< T > *node)
void print_tree(Tree_Node< T > *node, int depth=0)
int main()
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
void next()
Advance all underlying iterators (bounds-checked).
Definition ah-zip.H:175
DynList< T > maps(const C &c, Op op)
Classic map operation.
STL namespace.
Arc of graph implemented with double-linked adjacency lists.
Definition tpl_graph.H:222
Generic graph and digraph implementations.
Utility algorithms and operations for graphs.
General tree (n-ary tree) node.