examples: add 2 more graph search examples (DFS and BFS), move them into `examples/graphs` (#14131)
							parent
							
								
									5dce091379
								
							
						
					
					
						commit
						a2db44bc38
					
				| 
						 | 
				
			
			@ -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']
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue