From a2db44bc382cda54e690aa638105a5d6931c4ab5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claudio=20Cesar=20de=20S=C3=A1?= Date: Fri, 22 Apr 2022 06:01:29 -0300 Subject: [PATCH] examples: add 2 more graph search examples (DFS and BFS), move them into `examples/graphs` (#14131) --- examples/{ => graphs}/bfs.v | 30 +++++------ examples/graphs/bfs2.v | 92 ++++++++++++++++++++++++++++++++ examples/graphs/dfs.v | 103 ++++++++++++++++++++++++++++++++++++ 3 files changed, 210 insertions(+), 15 deletions(-) rename examples/{ => graphs}/bfs.v (100%) create mode 100644 examples/graphs/bfs2.v create mode 100644 examples/graphs/dfs.v diff --git a/examples/bfs.v b/examples/graphs/bfs.v similarity index 100% rename from examples/bfs.v rename to examples/graphs/bfs.v index ff20fdf867..99d7521ac7 100644 --- a/examples/bfs.v +++ b/examples/graphs/bfs.v @@ -1,3 +1,18 @@ +fn main() { + graph := { + 'A': ['B', 'C'] + 'B': ['A', 'D', 'E'] + 'C': ['A', 'F'] + 'D': ['B'] + 'E': ['B', 'F'] + 'F': ['C', 'E'] + } + println('Graph: $graph') + path := breadth_first_search_path(graph, 'A', 'F') + println('The shortest path from node A to node F is: $path') + assert path == ['A', 'C', 'F'] +} + // Breadth-First Search (BFS) allows you to find the shortest distance between two nodes in the graph. fn breadth_first_search_path(graph map[string][]string, vertex string, target string) []string { mut path := []string{} @@ -24,18 +39,3 @@ fn breadth_first_search_path(graph map[string][]string, vertex string, target st } return path } - -fn main() { - graph := { - 'A': ['B', 'C'] - 'B': ['A', 'D', 'E'] - 'C': ['A', 'F'] - 'D': ['B'] - 'E': ['B', 'F'] - 'F': ['C', 'E'] - } - println('Graph: $graph') - path := breadth_first_search_path(graph, 'A', 'F') - println('The shortest path from node A to node F is: $path') - assert path == ['A', 'C', 'F'] -} diff --git a/examples/graphs/bfs2.v b/examples/graphs/bfs2.v new file mode 100644 index 0000000000..8e8f92df85 --- /dev/null +++ b/examples/graphs/bfs2.v @@ -0,0 +1,92 @@ +// Author: ccs +// I follow literally code in C, done many years ago +fn main() { + // Adjacency matrix as a map + graph := { + 'A': ['B', 'C'] + 'B': ['A', 'D', 'E'] + 'C': ['A', 'F'] + 'D': ['B'] + 'E': ['B', 'F'] + 'F': ['C', 'E'] + } + println('Graph: $graph') + path := breadth_first_search_path(graph, 'A', 'F') + println('\n The shortest path from node A to node F is: $path.reverse()') +} + +// Breadth-First Search (BFS) allows you to find the shortest distance between two nodes in the graph. +fn breadth_first_search_path(graph map[string][]string, start string, target string) []string { + mut path := []string{} // ONE PATH with SUCCESS = array + mut queue := []string{} // a queue ... many paths + // all_nodes := graph.keys() // get a key of this map + n_nodes := graph.len // numbers of nodes of this graph + // a map to store all the nodes visited to avoid cycles + // start all them with False, not visited yet + mut visited := a_map_nodes_bool(n_nodes) // a map fully + // false ==> not visited yet: {'A': false, 'B': false, 'C': false, 'D': false, 'E': false} + queue << start // first arrival + for queue.len != 0 { + mut node := departure(mut queue) // get the front node and remove it + if visited[node] == false { // check if this node is already visited + // if no ... test it searchinf for a final node + visited[node] = true // means: visit this node + if node == target { + path = build_path_reverse(graph, start, node, visited) + return path + } + // Expansion of node removed from queue + print('\n Expansion of node $node (true/false): ${graph[node]}') + // take all nodes from the node + for vertex in graph[node] { // println("\n ...${vertex}") + // not explored yet + if visited[vertex] == false { + queue << vertex + } + } + print('\n QUEUE: $queue (only not visited) \n Visited: $visited') + } + } + path = ['Path not found, problem in the Graph, start or end nodes! '] + return path +} + +// Creating a map for VISITED nodes ... +// starting by false ===> means this node was not visited yet +fn a_map_nodes_bool(size int) map[string]bool { + mut my_map := map[string]bool{} // look this map ... + base := u8(65) + mut key := base.ascii_str() + for i in 0 .. size { + key = u8(base + i).ascii_str() + my_map[key] = false + } + return my_map +} + +// classical removing of a node from the start of a queue +fn departure(mut queue []string) string { + mut x := queue[0] + queue.delete(0) + return x +} + +// Based in the current node that is final, search for its parent, already visited, up to the root or start node +fn build_path_reverse(graph map[string][]string, start string, final string, visited map[string]bool) []string { + print('\n\n Nodes visited (true) or no (false): $visited') + array_of_nodes := graph.keys() + mut current := final + mut path := []string{} + path << current + + for (current != start) { + for i in array_of_nodes { + if (current in graph[i]) && (visited[i] == true) { + current = i + break // the first ocurrence is enough + } + } + path << current // update the path tracked + } + return path +} diff --git a/examples/graphs/dfs.v b/examples/graphs/dfs.v new file mode 100644 index 0000000000..ba7abbb90a --- /dev/null +++ b/examples/graphs/dfs.v @@ -0,0 +1,103 @@ +// Author: ccs +// I follow literally code in C, done many years ago + +fn main() { + // Adjacency matrix as a map + // Example 01 + graph_01 := { + 'A': ['B', 'C'] + 'B': ['A', 'D', 'E'] + 'C': ['A', 'F'] + 'D': ['B'] + 'E': ['F', 'B', 'F'] + 'F': ['C', 'E'] + } + // Example 02 + graph_02 := { + 'A': ['B', 'C', 'D'] + 'B': ['E'] + 'C': ['F'] + 'D': ['E'] + 'E': ['H'] + 'F': ['H'] + 'G': ['H'] + 'H': ['E', 'F', 'G'] + } + // println('Graph: $graph') + path_01 := depth_first_search_path(graph_01, 'A', 'F') + println('\n Graph_01: a first path from node A to node F is: $path_01.reverse()') + path_02 := depth_first_search_path(graph_02, 'A', 'H') + println('\n Graph_02: a first path from node A to node F is: $path_02.reverse()') +} + +// Depth-First Search (BFS) allows you to find a path between two nodes in the graph. +fn depth_first_search_path(graph map[string][]string, start string, target string) []string { + mut path := []string{} // ONE PATH with SUCCESS = array + mut stack := []string{} // a stack ... many nodes + // all_nodes := graph.keys() // get a key of this map + n_nodes := graph.len // numbers of nodes of this graph + mut visited := a_map_nodes_bool(n_nodes) // a map fully + // false ... not visited yet: {'A': false, 'B': false, 'C': false, 'D': false, 'E': false} + + stack << start // first push on the stack + for stack.len > 0 { + mut node := stack.pop() // get the top node and remove it from the stack + + // check if this node is already visited + if visited[node] == false { + // if no ... test it searchin for a final node + visited[node] = true // means: node visited + if node == target { + path = build_path_reverse(graph, start, node, visited) + return path + } + // Exploring of node removed from stack and add its relatives + print('\n Exploring of node $node (true/false): ${graph[node]}') + // graph[node].reverse() take a classical choice for DFS + // at most os left in this case. + // use vertex in graph[node] the choice is right + + // take all nodes from the node + for vertex in graph[node].reverse() { + // println("\n ...${vertex}") + // not explored yet + if visited[vertex] == false { + stack << vertex + } + } + print('\n Stack: $stack (only not visited) \n Visited: $visited') + } + } + path = ['Path not found, problem in the Graph, start or end nodes! '] + return path +} + +// Creating a map for nodes not VISITED visited ... +// starting by false ===> means this node was not visited yet +fn a_map_nodes_bool(size int) map[string]bool { + mut my_map := map[string]bool{} // look this map ... + for i in 0 .. size { + my_map[u8(65 + i).ascii_str()] = false + } + return my_map +} + +// Based in the current node that is final, search for his parent, that is already visited, up to the root or start node +fn build_path_reverse(graph map[string][]string, start string, final string, visited map[string]bool) []string { + print('\n\n Nodes visited (true) or no (false): $visited') + array_of_nodes := graph.keys() + mut current := final + mut path := []string{} + path << current + + for current != start { + for i in array_of_nodes { + if (current in graph[i]) && (visited[i] == true) { + current = i + break // the first ocurrence is enough + } + } + path << current // updating the path tracked + } + return path +}