| 1 | // Author: CCS |
| 2 | // I follow literally code in C, done many years ago |
| 3 | fn main() { |
| 4 | // Adjacency matrix as a map |
| 5 | graph := { |
| 6 | 'A': ['B', 'C'] |
| 7 | 'B': ['A', 'D', 'E'] |
| 8 | 'C': ['A', 'F'] |
| 9 | 'D': ['B'] |
| 10 | 'E': ['B', 'F'] |
| 11 | 'F': ['C', 'E'] |
| 12 | } |
| 13 | println('Graph: ${graph}') |
| 14 | path := breadth_first_search_path(graph, 'A', 'F') |
| 15 | println('\n The shortest path from node A to node F is: ${path.reverse()}') |
| 16 | } |
| 17 | |
| 18 | // Breadth-First Search (BFS) allows you to find the shortest distance between two nodes in the graph. |
| 19 | fn breadth_first_search_path(graph map[string][]string, start string, target string) []string { |
| 20 | mut path := []string{} // ONE PATH with SUCCESS = array |
| 21 | mut queue := []string{} // a queue ... many paths |
| 22 | // all_nodes := graph.keys() // get a key of this map |
| 23 | // a map to store all the nodes visited to avoid cycles |
| 24 | // start all them with False, not visited yet |
| 25 | mut visited := visited_init(graph) // a map fully |
| 26 | // false ==> not visited yet: {'A': false, 'B': false, 'C': false, 'D': false, 'E': false} |
| 27 | queue << start // first arrival |
| 28 | for queue.len != 0 { |
| 29 | mut node := departure(mut queue) // get the front node and remove it |
| 30 | if visited[node] == false { // check if this node is already visited |
| 31 | // if no ... test it searchinf for a final node |
| 32 | visited[node] = true // means: visit this node |
| 33 | if node == target { |
| 34 | path = build_path_reverse(graph, start, node, visited) |
| 35 | return path |
| 36 | } |
| 37 | // Expansion of node removed from queue |
| 38 | print('\n Expansion of node ${node} (true/false): ${graph[node]}') |
| 39 | // take all nodes from the node |
| 40 | for vertex in graph[node] { // println("\n ...${vertex}") |
| 41 | // not explored yet |
| 42 | if visited[vertex] == false { |
| 43 | queue << vertex |
| 44 | } |
| 45 | } |
| 46 | print('\n QUEUE: ${queue} (only not visited) \n Visited: ${visited}') |
| 47 | } |
| 48 | } |
| 49 | path = ['Path not found, problem in the Graph, start or end nodes! '] |
| 50 | return path |
| 51 | } |
| 52 | |
| 53 | // classical removing of a node from the start of a queue |
| 54 | fn departure(mut queue []string) string { |
| 55 | mut x := queue[0] |
| 56 | queue.delete(0) |
| 57 | return x |
| 58 | } |
| 59 | |
| 60 | // Creating aa map to initialize with of visited nodes .... all with false in the init |
| 61 | // so these nodes are NOT VISITED YET |
| 62 | fn visited_init(a_graph map[string][]string) map[string]bool { |
| 63 | mut temp := map[string]bool{} // attention in these initializations with maps |
| 64 | for i, _ in a_graph { |
| 65 | temp[i] = false |
| 66 | } |
| 67 | return temp |
| 68 | } |
| 69 | |
| 70 | // Based in the current node that is final, search for its parent, already visited, up to the root or start node |
| 71 | fn build_path_reverse(graph map[string][]string, start string, final string, visited map[string]bool) []string { |
| 72 | print('\n\n Nodes visited (true) or no (false): ${visited}') |
| 73 | array_of_nodes := graph.keys() |
| 74 | mut current := final |
| 75 | mut path := []string{} |
| 76 | path << current |
| 77 | |
| 78 | for (current != start) { |
| 79 | for i in array_of_nodes { |
| 80 | if current in graph[i] && visited[i] == true { |
| 81 | current = i |
| 82 | break // the first occurrence is enough |
| 83 | } |
| 84 | } |
| 85 | path << current // update the path tracked |
| 86 | } |
| 87 | return path |
| 88 | } |
| 89 | |
| 90 | //====================================================== |
| 91 | |