| 1 | // Author: CCS |
| 2 | // I follow literally code in C, done many years ago |
| 3 | |
| 4 | fn main() { |
| 5 | // Adjacency matrix as a map |
| 6 | // Example 01 |
| 7 | graph_01 := { |
| 8 | 'A': ['B', 'C'] |
| 9 | 'B': ['A', 'D', 'E'] |
| 10 | 'C': ['A', 'F'] |
| 11 | 'D': ['B'] |
| 12 | 'E': ['F', 'B', 'F'] |
| 13 | 'F': ['C', 'E'] |
| 14 | } |
| 15 | // Example 02 |
| 16 | graph_02 := { |
| 17 | 'A': ['B', 'C', 'D'] |
| 18 | 'B': ['E'] |
| 19 | 'C': ['F'] |
| 20 | 'D': ['E'] |
| 21 | 'E': ['H'] |
| 22 | 'F': ['H'] |
| 23 | 'G': ['H'] |
| 24 | 'H': ['E', 'F', 'G'] |
| 25 | } |
| 26 | path_01 := depth_first_search_path(graph_01, 'A', 'F') |
| 27 | println('\n Graph_01: a first path from node A to node F is: ${path_01.reverse()}') |
| 28 | path_02 := depth_first_search_path(graph_02, 'A', 'H') |
| 29 | println('\n Graph_02: a first path from node A to node H is: ${path_02.reverse()}') |
| 30 | } |
| 31 | |
| 32 | // Depth-First Search (BFS) allows you to find a path between two nodes in the graph. |
| 33 | fn depth_first_search_path(graph map[string][]string, start string, target string) []string { |
| 34 | mut path := []string{} // ONE PATH with SUCCESS = array |
| 35 | mut stack := []string{} // a stack ... many nodes |
| 36 | // all_nodes := graph.keys() // get a key of this map |
| 37 | mut visited := visited_init(graph) // a map fully with false in all vertex |
| 38 | // false ... not visited yet: {'A': false, 'B': false, 'C': false, 'D': false, 'E': false} |
| 39 | |
| 40 | stack << start // first push on the stack |
| 41 | for stack.len > 0 { |
| 42 | mut node := stack.pop() // get the top node and remove it from the stack |
| 43 | |
| 44 | // check if this node is already visited |
| 45 | if visited[node] == false { |
| 46 | // if no ... test it and search for a final node |
| 47 | visited[node] = true // means: node visited |
| 48 | if node == target { |
| 49 | path = build_path_reverse(graph, start, node, visited) |
| 50 | return path |
| 51 | } |
| 52 | // Exploring of node removed from stack and add its relatives |
| 53 | print('\n Exploring of node ${node} (true/false): ${graph[node]}') |
| 54 | // graph[node].reverse() take a classical choice for DFS |
| 55 | // at most os left in this case. |
| 56 | // use vertex in graph[node] the choice is right |
| 57 | |
| 58 | // take all nodes from the node |
| 59 | for vertex in graph[node].reverse() { |
| 60 | // println("\n ...${vertex}") |
| 61 | // not explored yet |
| 62 | if visited[vertex] == false { |
| 63 | stack << vertex |
| 64 | } |
| 65 | } |
| 66 | print('\n Stack: ${stack} (only not visited) \n Visited: ${visited}') |
| 67 | } |
| 68 | } |
| 69 | path = ['Path not found, problem in the Graph, start or end nodes! '] |
| 70 | return path |
| 71 | } |
| 72 | |
| 73 | // Creating aa map to initialize with of visited nodes .... all with false in the init |
| 74 | // so these nodes are NOT VISITED YET |
| 75 | fn visited_init(a_graph map[string][]string) map[string]bool { |
| 76 | mut temp := map[string]bool{} // attention in these initializations with maps |
| 77 | for i, _ in a_graph { |
| 78 | temp[i] = false |
| 79 | } |
| 80 | return temp |
| 81 | } |
| 82 | |
| 83 | // Based in the current node that is final, search for his parent, that is already visited, up to the root or start node |
| 84 | fn build_path_reverse(graph map[string][]string, start string, final string, visited map[string]bool) []string { |
| 85 | print('\n\n Nodes visited (true) or no (false): ${visited}') |
| 86 | array_of_nodes := graph.keys() |
| 87 | mut current := final |
| 88 | mut path := []string{} |
| 89 | path << current |
| 90 | |
| 91 | for current != start { |
| 92 | for i in array_of_nodes { |
| 93 | if current in graph[i] && visited[i] == true { |
| 94 | current = i |
| 95 | break // the first occurrence is enough |
| 96 | } |
| 97 | } |
| 98 | path << current // updating the path tracked |
| 99 | } |
| 100 | return path |
| 101 | } |
| 102 | |
| 103 | //***************************************************** |
| 104 | |