Graph search
In many cases, we need to traverse the graph to get some properties of the graph. For example, it is a very common requirement to find out all vertices connected with the specified vertices in the graph, or to determine whether a vertex is connected with the specified vertices.
For graph search, the most classic algorithms are depth first search and breadth first search. Next, we will explain these two search algorithms respectively.
- Depth first search
- Breadth first search
Depth first search
Find the child node first, and then the brother node
- The so-called depth first search means that when searching, if a node has both child nodes and brother nodes, find the child nodes first and then the brother nodes.
Obviously, because the edge has no direction, if 4 and 5 vertices are connected, then 4 will appear in the adjacent list of 5 and 5 will also appear in the adjacent list of 4. In order not to search the vertices repeatedly, there should be corresponding marks to indicate whether the current vertex has been searched. You can use a Boolean array boolean[V] marked, and the index represents the vertex, The value represents whether the current vertex has been searched. If it has been searched, it is marked as true. If it has not been searched, it is marked as false;
API design:
code:
Figure Graph
package graph; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedDeque; public class Graph { //Number of vertices private final int V; //Number of edges private int E; //Adjacency table private Queue<Integer>[] adj; public Graph(int V){ //Number of initialization vertices this.V = V; //Number of initialization edges this.E = 0; //Initialize adjacency table this.adj = new Queue[V]; for (int i = 0; i < adj.length; i++) { adj[i] = new ConcurrentLinkedDeque<>(); } } //Gets the number of vertices public int V(){ return V; } //Gets the number of edges public int E(){ return E; } //Add an edge to the graph v-w public void addEdge(int v, int w) { //In an undirected graph, the edge has no direction, so the edge can be either an edge from V to w or an edge from w to v. therefore, it is necessary to make w appear in the adjacency table of V and V appear in the adjacency table of W adj[v].offer(w); adj[w].offer(v); //Number of sides + 1 E++; } //Gets all vertices adjacent to vertex v public Queue<Integer> adj(int v){ return adj[v]; } }
Depth first search - DepthFirstSearch
package graph; public class DepthFirstSearch { //The index represents the vertex, and the value indicates whether the current vertex has been searched private boolean[] marked; //Record how many vertices are connected to the s vertex private int count; //Construct a depth first search object, and use depth first search to find all adjacent vertices of s vertex in G graph public DepthFirstSearch(Graph G,int s){ //Initialize marked array this.marked = new boolean[G.V()]; //Initializes the number of vertices communicating with vertex s this.count=0; dfs(G,s); } //Use depth first search to find all connected vertices of v vertices in G graph private void dfs(Graph G, int v){ //Identify v vertices as searched marked[v] = true; for (Integer w : G.adj(v)) { //Judge whether the current w vertex has been searched. If not, recursively call the dfs method for depth search if (!marked[w]){ dfs(G,w); } } //Number of connected vertices + 1 count++; } //Judge whether the w vertex is connected with the s vertex public boolean marked(int w){ return marked[w]; } //Gets the total number of all vertices connected to vertex s public int count(){ return count; } }
test
public class DepthFirstSearchTest { public static void main(String[] args) { //Preparing Graph objects Graph G = new Graph(13); G.addEdge(0,5); G.addEdge(0,1); G.addEdge(0,2); G.addEdge(0,6); G.addEdge(5,3); G.addEdge(5,4); G.addEdge(3,4); G.addEdge(4,6); G.addEdge(7,8); G.addEdge(9,11); G.addEdge(9,10); G.addEdge(9,12); G.addEdge(11,12); //Prepare depth first search objects DepthFirstSearch search = new DepthFirstSearch(G, 0); //Test the number of vertices connected to a vertex int count = search.count(); System.out.println("The number of vertices communicating with the starting point 0 is:"+count); //Test whether a vertex is the same as the starting point boolean marked1 = search.marked(5); System.out.println("Whether vertex 5 and vertex 0 are connected:"+marked1); boolean marked2 = search.marked(7); System.out.println("Whether vertex 7 and vertex 0 are connected:"+marked2); } }
Breadth first search
First find the sibling node, and then find the child node
- The so-called depth first search means that when searching, if a node has both child nodes and brother nodes, first find the brother nodes, and then find the child nodes.
API design:
Train of thought analysis:
It is the same as the sequence traversal of binary tree -- breadth first
Tree – 05 - binary tree – 02 - binary search tree (BST) traversal
Implementation steps:
- Create a queue to store the nodes of each layer;
- Use the loop to pop up a node from the queue:
- Get the key of the current node;
- If the left child node of the current node is not empty, the left child node is put into the queue
- If the right child node of the current node is not empty, the right child node is put into the queue
Create a secondary queue for sorting
code:
package graph; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedDeque; public class BreadthFirstSearch { //The index represents the vertex, and the value indicates whether the current vertex has been searched private boolean[] marked; //Record how many vertices are connected to the s vertex private int count; //The point used to store the adjacency table to be searched private Queue<Integer> waitSearch; //Construct a breadth first search object, and use breadth first search to find all adjacent vertices of s vertex in G graph public BreadthFirstSearch(Graph G, int s) { this.marked = new boolean[G.V()]; this.count=0; this.waitSearch = new ConcurrentLinkedDeque<Integer>(); bfs(G,s); } //Use breadth first search to find all adjacent vertices of v vertex in G graph private void bfs(Graph graph, int v){ //Identifies the current vertex v as searched marked[v] = true; //Let vertex v enter the queue to be searched waitSearch.offer(v); System.out.print("node"+v+"The breadth traversal order is:"+v); //Through the loop, if the queue is not empty, a vertex to be searched will pop up from the queue for search while (!waitSearch.isEmpty()){ //Pop up a vertex to search Integer wait = waitSearch.poll(); //Traversing the adjacency table of wait vertices for(Integer w: graph.adj(wait)){ // The vertex has not been searched yet. Search it if(!marked(w)){ marked[w] = true; // The node is put on the stack for subsequent acquisition of the child nodes of the node waitSearch.offer(w); //Let the connected vertex + 1; count++; System.out.print( +w); } } } System.out.println(); } //Judge whether the w vertex is connected with the s vertex public boolean marked(int w) { return marked[w]; } //Gets the total number of all vertices connected to vertex s public int count() { return count; } }
Test:
package graph; public class BreadthFirstSearchTest { public static void main(String[] args) { //Preparing Graph objects Graph G = new Graph(13); G.addEdge(0,5); G.addEdge(0,1); G.addEdge(0,2); G.addEdge(0,6); G.addEdge(5,3); G.addEdge(5,4); G.addEdge(3,4); G.addEdge(4,6); G.addEdge(7,8); G.addEdge(9,11); G.addEdge(9,10); G.addEdge(9,12); G.addEdge(11,12); //Prepare breadth first search objects BreadthFirstSearch search = new BreadthFirstSearch(G, 0); //Test the number of vertices connected to a vertex int count = search.count(); System.out.println("The number of vertices communicating with the starting point 0 is:"+count); //Test whether a vertex is the same as the starting point boolean marked1 = search.marked(5); System.out.println("Whether vertex 5 and vertex 0 are connected:"+marked1); boolean marked2 = search.marked(7); System.out.println("Whether vertex 7 and vertex 0 are connected:"+marked2); } }
Case - unblocked project continued 1
- A province investigates the urban traffic conditions and obtains the statistical table of existing urban roads, which lists the cities and towns directly connected by each road. The goal of the provincial government's "unblocked project" is to enable any two cities and towns in the province to realize traffic (but there is not necessarily a direct road connection, as long as they can reach each other indirectly through the road). According to the current road conditions, are city 9 and city 10 connected? Are cities 9 and 8 interlinked?
- There is a trffic in our test data folder_ Project.txt file, which is the statistical table of Zhengzheng road. The following is the interpretation of the data:
Requirements:
- There are 20 cities in total. At present, 7 roads have been modified. Do cities 9 and 10 are connected? Are cities 9 and 8 interlinked?
Problem solving ideas:
- Create a Graph object to represent the city;
- Call AddEdge (0,1), AddEdge (6,9), AddEdge (3,8), AddEdge (5,11), AddEdge (2,12), AddEdge (6,10) and AddEdge (4,8) respectively, indicating that the built roads connect the corresponding cities;
- Construct DepthFirstSearch object or BreadthFirstSearch object through Graph object and vertex 9;
- By calling the marked(10) and marked(8) methods of the search object, we can get whether the 9 and city are connected with the 10 city and whether the 9 city is connected with the 8 city.
code:
package graph; import java.io.BufferedReader; import java.io.InputStreamReader; public class Traffic_Project_Test2 { public static void main(String[] args) throws Exception{ //Build a buffered read stream BufferedReader BufferedReader br = new BufferedReader(new InputStreamReader(Traffic_Project_Test2.class.getClassLoader().getResourceAsStream("traffic_project.txt"))); //Read the first row of data 20 int totalNumber = Integer.parseInt(br.readLine()); //Build a Graph object Graph G = new Graph(totalNumber); //Read the second row of data 7 int roadNumber = Integer.parseInt(br.readLine()); //Cycle reading for a limited number of times (7) to read the built road for (int i = 1;i<=roadNumber;i++){ String road = br.readLine();//"0 1" String[] str = road.split(" "); int v = Integer.parseInt(str[0]); int w = Integer.parseInt(str[1]); //Call the addEdge method of the graph to add an edge to the graph to represent the built road G.addEdge(v,w); } //Build a depth first search object with the starting point set to vertex 9 DepthFirstSearch search = new DepthFirstSearch(G, 9); //Call the marked method to judge whether the 8 vertices and 10 vertices are connected with the starting point 9 System.out.println("Whether vertex 8 and vertex 9 are connected:"+search.marked(8)); System.out.println("Whether vertex 10 and vertex 9 are connected:"+search.marked(10)); } }
Path lookup
In real life, map is a tool we often use. Usually, we use it for navigation, enter a departure city and a destination city, and we can plan the route. On the planned route, we will pass through many middle cities. Such questions are translated into professional questions:
Requirements:
Is there a path from s vertex to v vertex? If so, find this path.
- For example, in the above figure, the path from vertex 0 to vertex 4 is marked in red, so we can represent the path as 0-2-3-4.
Find API design
Train of thought analysis:
- When we implement path search, the most basic operation is to traverse and search the graph. Therefore, our implementation is based on depth first search for the time being. The search process is relatively simple. We added edgeTo [] integer array, which will record the path from each vertex to the starting point s.
If we set the vertex to 0, its search can be represented as the following figure:
According to the result of the final edgeTo, we can easily find the path from the starting point 0 to any vertex;
code:
DepthFirstPaths
import java.util.Stack; public class DepthFirstPaths { //The index represents the vertex, and the value indicates whether the current vertex has been searched private boolean[] marked; //starting point private int s; //The index represents the vertex, and the value represents the last vertex on the path from the starting point s to the current vertex private int[] edgeTo; //Construct the depth first search object, and use the depth first search to find all paths with the starting point of s in the G graph public DepthFirstPaths(Graph G, int s){ //Initialize marked array this.marked = new boolean[G.V()]; //Initialization starting point this.s = s; //Initialize edgeTo array this.edgeTo = new int[G.V()]; dfs(G,s); } //Use depth first search to find all adjacent vertices of v vertex in G graph private void dfs(Graph G, int v){ //Represent v as searched marked[v] = true; //Traverse the adjacency table of vertex v, get each adjacent vertex, and continue the recursive search for (Integer w : G.adj(v)) { //If the vertex w is not searched, the recursive search continues if (!marked[w]){ edgeTo[w] = v;//The last vertex on the path to vertex w is v dfs(G,w); } } } //Judge whether there is a path between w vertex and s vertex public boolean hasPathTo(int v){ return marked[v]; } //Find the path from the starting point s to the vertex v (that is, the vertex through which the path passes) public Stack<Integer> pathTo(int v){ if (!hasPathTo(v)){ return null; } //Create a stack object and save all vertices in the path Stack<Integer> path = new Stack<>(); //Through the loop, start from vertex v and look forward until you find the starting point for (int x = v; x!=s;x = edgeTo[x]){ path.push(x); } //Put the starting point s on the stack path.push(s); return path; } }
Test code
package graph; import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.Stack; public class DepthFirstPathsTest { public static void main(String[] args) throws Exception{ //Building a buffered read stream BufferedReader BufferedReader br = new BufferedReader(new InputStreamReader(DepthFirstPathsTest.class.getClassLoader().getResourceAsStream("main/resources/road_find.txt"))); //Read the first row of data 6 int total = Integer.parseInt(br.readLine()); //Build a Graph based on the first row of data Graph G = new Graph(total); //Read the second row of data 8 int edgeNumbers = Integer.parseInt(br.readLine()); //Continue to read the two vertices associated with each edge through the loop, and call the addEdge method to add the edge for (int i = 1;i<=edgeNumbers;i++){ String edge = br.readLine();//0 1 String[] str = edge.split(" "); int v = Integer.parseInt(str[0]); int w = Integer.parseInt(str[1]); G.addEdge(v,w); } //Build a path to find the object and set the starting point to 0 graph.DepthFirstPaths paths = new graph.DepthFirstPaths(G, 0); //Call pathTo(4), find the path from start point 0 to end point 4, and return to Stack Stack<Integer> path = paths.pathTo(4); StringBuilder sb = new StringBuilder(); //Traversal stack object for (Integer v : path) { sb.append(v+"-"); } sb.deleteCharAt(sb.length()-1); System.out.println(sb); } }