Basic algorithm set of graph (shortest path and minimum spanning tree)
1. Shortest path algorithm (DFS Freud dijestra Bellman Ford)
1.1DFS two-point shortest path
In fact, not only depth first search, but also breadth first search can solve such problems. DFS is introduced here. DFS and BFS are not much different in solving the problem of the shortest path
Idea: access all the edges adjacent to the starting node from the starting node, and then continue to search until we reach the cur node we want, and maintain a shortest path variable to get the answer.
import java.util.Scanner; public class Map { static int n,m,min,finalN;//They are the number of nodes, the number of edges, the minimum value maintained in the process and the point we are looking for static int inf=99999999;//The artificial maximum can be regarded as infinity, that is, there is no path between two nodes static int[][] edge=new int[100][100];//Adjacency matrix graph storage array static int[] mark=new int[100];//Tag array public static void dfs(int cur,int pathLength){ if(min<pathLength) return;//Termination condition 1 the length of the current path has exceeded the minimum value maintained //Termination condition 2: the current node has arrived at the final node we are looking for if(cur==finalN){ if(min>pathLength) min=pathLength;//Maintain the minimum value and return return; } else{ for(int i=1;i<=n;i++)//The dfs for loop partially traverses all possible { //If there is a path and it has not been passed and it is not the current node if(edge[cur][i]!=inf && edge[cur][i]!=0 &&mark[i]==0){ mark[i]=1; dfs(i,pathLength+edge[cur][i]); mark[i]=0;//dfs backtracking process } } } return; } public static void main(String[] args){ Scanner sc=new Scanner(System.in); n=sc.nextInt(); m=sc.nextInt(); finalN=sc.nextInt(); //During the initialization of adjacency matrix, we make the distance between each two nodes equal to inf, and then let each node go to its own path of 0 for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ edge[i][j]=inf; } edge[i][i]=0; } int a,b; //Save the edge we want to enter while(m--!=0) { a=sc.nextInt(); b=sc.nextInt(); edge[a][b]=sc.nextInt(); } min=inf; mark[1]=1; dfs(1,0); System.out.println(min); } }
To check the two points, we only need to control the starting condition of dfs and the value of finalN. However, careful partners may find that the condition for our loop exit is that the current path is greater than the minimum path value maintained, so we won't carry out the next for loop, but think about it. If there are negative edges in our graph, Then, the current path value may be smaller than the shortest path after cycling. Therefore, when we add this termination condition to dfs, we can't maintain the edge with negative weight. Therefore, if we have an edge with negative weight, we shouldn't let the cycle terminate because of this. We can only add termination condition 2, Here, we don't consider that the edge adjacent to the final node has negative weight. Think about it. If we can take a negative edge through the end point and then return to the end point, if the distance can be reduced, the sum of path values can be reduced all the time, and there is no research value.
1.2 Freudian algorithm multi-source shortest path
Freud algorithm is the most violent algorithm to solve the shortest path in my opinion, but it can solve the shortest path of multiple sources, that is, the shortest path between any two points can be obtained, and the code is also very simple.
Idea: first, we only allow transit through node 1 to find the shortest path of each node, and then we allow transit through nodes 1 and 2 to find the shortest path repeatedly. Until transit through all nodes is allowed, the shortest path we maintain is the strict shortest path, We can solve some graphs with negative edges by Freud algorithm. If it is a negative one-way edge, it is OK. If it is a negative loop, it cannot be solved. In other words, there is no minimum value in the negative weight loop
Only the core code is attached here. The process of other construction diagrams is the same as the first one
public static void Floyd(){ //The first cycle represents which point is used as the transfer point. Because every time we maintain the outermost cycle, we use one point as the transfer point //Therefore, we can use the for loop to simulate the process of taking point 1 as the transfer, and taking points 1 and 2 as the transfer //Generally speaking, after the first round of maintenance, it is equivalent to the shortest path obtained after each point passes through point 1, and it is retained. When we maintain it again, we take point 2 as the transit //Is that equivalent to the minimum value of 1 and 2 for each point? Right for(int k = 1; k <= n; k++){ //The latter two loops are equivalent to traversing the matrix for(int i = 1; i <= n; i++){ for(int j = 1; j <= n; j++){ //Operation to update values if(edge[i][k] < inf && edge[k][j] < inf && edge[i][j] > edge[i][k] + edge[k][j]) edge[i][j] = edge[i][k] + edge[k][j]; } } } }
1.3 Dijstra algorithm single source shortest path
If Freud is a planning idea, then dijestra is a greedy algorithm
It should be noted that dijestra is a greedy algorithm, so some cases cannot be calculated. It cannot solve the graph with negative weight edges! The specific reasons will be explained below
Idea: we start from a node and find the closest edge to the node every time. We think that this edge does not need to be maintained, which is the shortest value. Then we mark this point to indicate that it has been used, and then use this point as the transit point to update the values of other paths, and then take the shortest edge and repeat the above operations, Think about whether it is perfect if all edges are positive. You can find the value of the shortest path from the starting point to all other points, but what if some edges are negative?
For example, our current value from node 1 to node 3 is - 2, the value from node 1 to node 2 is - 1, and the value from node 2 to node 3 is - 2. Obviously, when we start from node 1, we will not go to the edge with the value of - 1 and then update to get the value of 1-3 as - 3, but directly select the edge with the value of 1-3 as - 2 for the next operation, so we can't solve the problem with negative values
public static int[] Dijkstra(int start){ //start is the starting point //dis is the last required single source shortest path array int[] dis=new int[n+1]; //Initialize dis array for(int i=1;i<=n;i++) dis[i]=edge[start][i]; //Mark the starting point has passed mark[start]=1; //Maintain another point corresponding to the shortest edge obtained each time int u=-1; //Maintain the shortest edge and record the smallest node for(int i=1;i<=n-1;i++) { min = inf; for (int j = 1; j <= n; j++) { if (mark[j] == 0 && dis[j] < min) { min = dis[j]; u = j; } } mark[u]=1; //Perform another round of relaxation operation at the minimum point for (int k = 1; k <= n; k++) { if (edge[u][k] < inf && mark[k] == 0) { if (dis[k] > dis[u] + edge[u][k]) dis[k] = dis[u] + edge[u][k]; } } } return dis; }
1.4bellman Floyd solves the problem with negative weighted edges
Bellman Floyd algorithm seems to me like the idea of BFS
Idea: we store the graph into three arrays. Here we set it as s[] e[] w []
They are the weights of the starting point and the ending point, and then each time we retrieve all the edges for relaxation operation. After n-1 rounds of relaxation, we can get the single source shortest path we want, and this algorithm will not have the problem of calculation errors of negative weighted edges that dijestra will have. You may not understand the idea. I will make a statement in the code
public static int[] Bellman_Floyd(int start){ int[] dis=new int[n+1]; for(int i=1;i<=n;i++) dis[i]=inf; //Initialization allows you to go to yourself dis[start]=0; //If there is an edge that can be reached from the starting point, we can get the shortest path from the starting point to other points because dis[start]=0 in the first cycle //If we continue the cycle, the distance we can reach in one step has been updated, then we can get the point of taking two steps through the second cycle. That's what I understand. You can also simulate yourself for(int j=1;j<=n-1;j++) { for(int i=1;i<=m;i++) { if(dis[e[i]]>dis[s[i]]+w[i]) dis[e[i]]=dis[s[i]]+w[i]; } } return dis; }
In addition, this algorithm can also know whether there is a negative weight loop. Assuming that we take n steps and take another step, if there is still a little distance to be updated, there will be a negative weight loop, but we say that if there is a negative weight loop, the research on the minimum value becomes meaningless. In practical problems, negative weights rarely appear
In addition, Bellman Floyd algorithm performs relaxation operations at most n-1 times. Assuming that the relaxation operation is meaningless, some nodes do not need to continue to relax when they have reached the minimum value, we can design a queue, Each time, only the nodes whose minimum value changes can be relaxed to simplify the time. This queue Optimization called Bellman Floyd algorithm can be consulted if you are interested
2. Minimum spanning tree
Of course, the minimum spanning tree can also use the search method to traverse all paths for calculation, but it takes a lot of time, so we introduce the following two common algorithms for the minimum spanning tree
2.1 Kruskal algorithm
Idea: first, we sort all edges by weight, and then select one by one. If the currently selected edge will not make our minimum spanning tree form a loop, add this edge and continue to select until n-1 edges
How do we know that the new edges added will not form a loop?
If we want to know that it will not form a loop, we need to know whether the two nodes of this edge have been used
If both nodes have been used, adding a new edge will lead to the generation failure of the minimum spanning tree. Therefore, we need to use a data structure called joint query set, that is, maintain the root of all nodes. At the beginning, we let the root of all nodes be ourselves, If you add an edge, make the end node of the edge subordinate to the start node. If the values of the two nodes are the same, it means that the two nodes are connected at this time
import java.util.Arrays; import java.util.Comparator; import java.util.Scanner; //Define a class edge class edge{ int u; int v; int w; @Override public String toString() { return "edge{" + "u=" + u + ", v=" + v + ", w=" + w + '}'; } } public class Kruskal { static edge[] e=new edge[100]; static int n,m; //Join set array static int[] f=new int[100]; static int sum=0,count=0; //And look up the set to get the method of ancestors public static int getf(int v) { if(f[v]==v) return v; else f[v]=getf(f[v]); return f[v]; } //The method of finding ancestors and deciding whether edges can be added public static int merge(int v,int u) { int t1; int t2; t1=getf(v); t2=getf(u); //If it is different, let the end node conform to start. Returning 1 means it is feasible if(t1!=t2) { f[t2]=t1; return 1; } return 0; } public static void main(String[] args) { Scanner sc=new Scanner(System.in); n=sc.nextInt(); m=sc.nextInt(); for(int i=1;i<=m;i++) { e[i]=new edge();//Custom type array initialization //Save edge e[i].u=sc.nextInt(); e[i].v=sc.nextInt(); e[i].w=sc.nextInt(); } //Rewrite interface sorting to sort [1,m] subscripts with w, i.e. weight as the object Arrays.sort(e,1,m+1, new Comparator<edge>() { @Override public int compare(edge o1, edge o2) { return o1.w-o2.w; } }); // System.out.println(Arrays.toString(e)); for(int i=1;i<=n;i++) f[i]=i; //Core part for(int i=1;i<=m;i++) { if(merge(e[i].u,e[i].v)==1) { count++; sum=sum+e[i].w; } //n-1 nodes exit if(count==n-1) break; } System.out.println(sum); } }
2.2 prim algorithm
Idea: take a node as the starting node, add the node with the shortest distance each time, update the distance with other nodes, and then add a node with the shortest distance until n nodes are added. Think about it. Is this very similar to dijestra's algorithm thinking
Dijestra maintains the same way, but finally maintains an array, that is, the single source shortest path. If we slightly modify this process, we can get the minimum spanning tree
public static int Prim(int start){ int sum=0;//Weights and to be maintained int count = 0; //start is the starting point //dis single source shortest path array int[] dis=new int[n+1]; //Initialize dis array for(int i=1;i<=n;i++) dis[i]=edge[start][i]; //Mark the starting point has passed mark[start]=1; //Maintain another point corresponding to the shortest edge obtained each time int u=-1; //Maintain the shortest edge and record the smallest node count++; //n nodes do not need to continue the cycle while(count<n) { min = inf; for (int j = 1; j <= n; j++) { if (mark[j] == 0 && dis[j] < min) { min = dis[j]; u = j; } } mark[u]=1;count++;sum+=dis[u]; //System.out.println(dis[u]); //Perform another round of relaxation operation at the minimum point //Unlike dijestra, our minimum spanning tree //Only the distance between two points to be relaxed is added each time //Instead of the distance from the start point to the end point, so the dis array //**Maintains the distance from the point to the currently generated tree** //Therefore, the update operation also changes accordingly for (int k = 1; k <= n; k++) { if (edge[u][k] < inf && mark[k] == 0) { if (dis[k] > edge[u][k]) dis[k] = edge[u][k]; } } } return sum; }
The above is my understanding of the four shortest path algorithms and the two minimum spanning tree algorithms in the graph
Ask for a wave of attention again~