Tip: after the article is written, the directory can be generated automatically. Please refer to the help document on the right for how to generate it
Greedy Algorithm
Greedy algorithm - Definition:
Use the segmentation theorem to find one edge of the minimum spanning tree and repeat until all edges of the minimum spanning tree are found
- Greedy algorithm is the basic algorithm for calculating the minimum spanning tree of a graph. Its basic principle is the segmentation theorem,
- Use the segmentation theorem to find one edge of the minimum spanning tree and repeat until all edges of the minimum spanning tree are found.
- If a graph has V vertices, it needs to find V-1 edges to represent the minimum spanning tree of the graph.
Greedy algorithm - principle:
Algorithm of minimum spanning tree
- There are many algorithms for calculating the minimum spanning tree of a graph, but these algorithms can be regarded as a special case of greedy algorithms,
- The difference between these algorithms is the way to save the segmentation and determine the cross cutting edge with the smallest weight.
Prim algorithm
- We learn the first method to calculate the minimum spanning tree, called Prim algorithm, which adds an edge to a spanning tree at each step. At first, the tree has only one vertex, and then V-1 edges will be added to it. Each time, the next edge with the lowest weight connecting the vertex in the tree and the vertex not in the tree will be added to the tree.
Segmentation rules:
- The vertices in the minimum spanning tree are regarded as a set, and the vertices not in the minimum spanning tree are regarded as another set.
Algorithm API design
Implementation principle of Prim algorithm
Prim algorithm always divides the vertices in the graph into two sets, the minimum spanning tree vertex and the non minimum spanning tree vertex. By constantly repeating some operations, you can gradually add the vertices in the non minimum spanning tree to the minimum spanning tree until all the vertices are added to the minimum spanning tree
- When designing the API, we use the minimum index priority queue to store the effective crosscutting edges of vertices in the tree and non vertices in the tree. How is it represented? We can let the index value of the minimum index priority queue represent the vertices of the graph, and let the value in the minimum index priority queue represent the edge weight from another vertex to the current vertex.
1. Initialization status
- In the initialization state, 0 is the only vertex in the minimum spanning tree by default, and other vertices are not in the minimum spanning tree. At this time, the crosscutting edge is the four edges 0-2,0-4,0-6,0-7 in the adjacency table of vertex 0. We only need to store the weight values of these edges at the indexes 2, 4, 6 and 7 of the index priority queue.
2. Find the edge with the smallest weight and add it to the tree
Now you just need to find the edge with the least weight from the four crosscutting edges, and then add the corresponding vertices. Therefore, the weight of the crosscutting edge 0-7 is the smallest, so the edge 0-7 is added. At this time, 0 and 7 belong to the vertices of the minimum spanning tree, and the others do not. Now the edges in the adjacent table of vertex 7 also become crosscutting edges,
Two operations are required:
- The 0-7 edge is no longer a crosscutting edge and needs to be invalidated: it can be completed by calling the delMin() method of the minimum index priority queue;
- 2 and 4 vertices each have two connections pointing to the minimum spanning tree, and only one needs to be reserved:
The weight of 4-7 is less than that of 0-4, so keep 4-7 and call change(4,0.37) of the index priority queue,
The weight of 0-2 is less than that of 2-7, so keep 0-2 and no additional operation is required.
3. Repeat the above action,
- By repeating the above actions, we can add all vertices to the minimum spanning tree.
Auxiliary class
1.Edge - edge
package graph.tu; public class Edge implements Comparable<Edge> { private final int v;//Vertex one private final int w;//Vertex two private final double weight;//The weight of the current edge //Construct an edge object from vertices v and w and weight values public Edge(int v, int w, double weight) { this.v = v; this.w = w; this.weight = weight; } //Gets the weight value of the edge public double weight(){ return weight; } //Gets a point on the edge public int either(){ return v; } //Gets another vertex on the edge except vertex vertex public int other(int vertex){ if (vertex==v){ return w; }else{ return v; } } @Override public int compareTo(Edge that) { //Use a traversal record to compare the results int cmp; if (this.weight()>that.weight()){ //If the weight value of the current edge is large, let cmp=1; cmp = 1; }else if (this.weight()<that.weight()){ //If the weight value of the current edge is small, let cmp=-1; cmp=-1; }else{ //If the weight value of the current edge is as large as that edge, let cmp=0 cmp = 0; } return cmp; } }
2.EdgeWeightedGraph - weighted undirected graph
package graph.tu; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; public class EdgeWeightedGraph { //Total number of vertices private final int V; //Total number of edges private int E; //Adjacency table private Queue<Edge>[] adj; //Create a null weighted undirected graph with V vertices public EdgeWeightedGraph(int v) { //Number of initialization vertices this.V = v; //Number of initialization edges this.E = 0; //Initialize adjacency table this.adj = new ConcurrentLinkedQueue[v]; for (int i = 0; i < adj.length; i++) { adj[i] = new ConcurrentLinkedQueue<Edge>(); } } //Gets the number of vertices in the graph public int V() { return V; } //Gets the number of edges in the graph public int E() { return E; } //Add an edge to the weighted undirected graph e public void addEdge(Edge e) { //You need to make edge e appear in the adjacency table of two vertices of edge e at the same time int v = e.either(); int w = e.other(v); adj[v].offer(e); adj[w].offer(e); //Number of sides + 1 E++; } //Gets all edges associated with the vertex v public Queue<Edge> adj(int v) { return adj[v]; } //Gets all edges of a weighted undirected graph public Queue<Edge> edges() { //Create a queue object to store all the edges Queue<Edge> allEdges = new ConcurrentLinkedQueue<>(); //Traverse each vertex in the graph and find the adjacency table of the vertex, which stores each edge associated with the vertex //Because this is an undirected graph, the same edge appears in the adjacency table of its associated two vertices at the same time. It is necessary to record an edge only once; for(int v =0;v<V;v++){ //Traverse the adjacency table of v vertices and find each edge associated with v for (Edge e : adj(v)) { if (e.other(v)<v){ allEdges.offer(e); } } } return allEdges; } }
3.IndexMinPriorityQueue - minimum priority queue
package graph.tu; public class IndexMinPriorityQueue<T extends Comparable<T>> { //Elements in the storage heap private T[] items; //Save the index of each element in the items array. The pq array needs to be heap ordered private int[] pq; //Save the reverse order of qp, the value of pq as the index and the index of pq as the value private int[] qp; //Record the number of elements in the heap private int N; public IndexMinPriorityQueue(int capacity) { this.items = (T[]) new Comparable[capacity+1]; this.pq = new int[capacity+1]; this.qp= new int[capacity+1]; this.N = 0; //By default, no data is stored in the queue, so that all elements in qp are - 1; for (int i = 0; i < qp.length; i++) { qp[i]=-1; } } //Gets the number of elements in the queue public int size() { return N; } //Determine whether the queue is empty public boolean isEmpty() { return N==0; } //Judge whether the element at index i in the heap is smaller than the element at index j private boolean less(int i, int j) { return items[pq[i]].compareTo(items[pq[j]])<0; } //Swap the values at the i and j indexes in the heap private void exch(int i, int j) { //Exchange data in pq int tmp = pq[i]; pq[i] = pq[j]; pq[j] = tmp; //Update data in qp qp[pq[i]]=i; qp[pq[j]] =j; } //Judge whether the element corresponding to k exists public boolean contains(int k) { return qp[k] !=-1; } //Index associated with the smallest element public int minIndex() { return pq[1]; } //Insert an element into the queue and associate the index i public void insert(int i, T t) { //Judge whether i has been associated. If it has been associated, it will not be inserted if (contains(i)){ return; } //Number of elements + 1 N++; //Store the data in the i location corresponding to the items items[i] = t; //Store i in pq pq[N] = i; //i in pq is recorded by qp qp[i]=N; //Adjust the heap by floating it up swim(N); } //Deletes the smallest element in the queue and returns the index associated with that element public int delMin() { //Gets the index associated with the smallest element int minIndex = pq[1]; //Swap elements at index 1 and maximum index in pq exch(1,N); //Delete the corresponding content in qp qp[pq[N]] = -1; //Delete content at pq maximum index pq[N]=-1; //Delete the corresponding content in items items[minIndex] = null; //Number of elements - 1 N--; //Subsidence adjustment sink(1); return minIndex; } //Delete index i associated elements public void delete(int i) { //Find the index of i in pq int k = qp[i]; //Swap the value at index k and the value at index N in pq exch(k,N); //Delete content in qp qp[pq[N]] = -1; //Delete content in pq pq[N]=-1; //Delete content in items items[k]=null; //Number of elements - 1 N--; //Heap adjustment sink(k); swim(k); } //Change the element associated with index i to t public void changeItem(int i, T t) { //Modify the element at i position in the items array to t items[i] = t; //Find where i appears in pq int k = qp[i]; //Heap adjustment sink(k); swim(k); } //Using the floating-up algorithm, the element at index k can be in a correct position in the heap private void swim(int k) { while(k>1){ if (less(k,k/2)){ exch(k,k/2); } k = k/2; } } //Using the sinking algorithm, the element at index k can be in a correct position in the heap private void sink(int k) { while(2*k<=N){ //Find the smaller value in the child node int min; if (2*k+1<=N){ if (less(2*k,2*k+1)){ min = 2*k; }else{ min = 2*k+1; } }else{ min = 2*k; } //Compare the current node with the smaller value if (less(k,min)){ break; } exch(k,min); k = min; } } }
Prim algorithm - PrimMST
package graph.tu; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; public class PrimMST { //The index represents the vertex, and the value represents the shortest edge between the current vertex and the minimum spanning tree private Edge[] edgeTo; //The index represents the vertex, and the value represents the weight of the shortest edge between the current vertex and the minimum spanning tree private double[] distTo; //The index represents the vertex. If the current vertex is already in the tree, the value is true; otherwise, it is false private boolean[] marked; //Stores valid crosscutting edges between vertices in the tree and non vertices in the tree private IndexMinPriorityQueue<Double> pq; //According to a weighted undirected graph, the minimum spanning tree calculation object is created public PrimMST(EdgeWeightedGraph G) { //Initialize edgeTo this.edgeTo = new Edge[G.V()]; //Initialize distTo this.distTo = new double[G.V()]; for (int i = 0; i < distTo.length; i++) { distTo[i] = Double.POSITIVE_INFINITY; } //Initialize marked this.marked = new boolean[G.V()]; //Initialize pq pq = new IndexMinPriorityQueue<Double>(G.V()); //By default, vertex 0 is allowed to enter the tree, but there is only one vertex 0 in the tree. Therefore, vertex 0 is not connected to other vertices by default, so let the value at the corresponding position of distTo store 0.0 distTo[0] = 0.0; pq.insert(0,0.0); //Traverse the index minimum priority queue, get the vertex corresponding to the minimum and N cutting edges, and add the vertex to the minimum spanning tree while (!pq.isEmpty()){ visit(G,pq.delMin()); } } //Add vertex v to the minimum spanning tree and update the data private void visit(EdgeWeightedGraph G, int v) { //Add vertex v to the minimum spanning tree marked[v] = true; //Update data for (Edge e : G.adj(v)) { //Get another vertex of edge e (current vertex is v) int w = e.other(v); //Judge whether another vertex is already in the tree. If it is in the tree, no processing will be done. If it is no longer in the tree, the data will be updated if (marked[w]){ continue; } //Judge whether the weight of edge e is less than the weight from w vertex to the smallest edge already existing in the tree; if (e.weight()<distTo[w]){ //Update data edgeTo[w] = e; distTo[w] = e.weight(); if (pq.contains(w)){ pq.changeItem(w,e.weight()); }else{ pq.insert(w,e.weight()); } } } } //Gets all edges of the minimum spanning tree public Queue<Edge> edges() { //Create queue object Queue<Edge> allEdges = new ConcurrentLinkedQueue<>(); //Traverse the edgeTo array to get each edge. If it is not null, it will be added to the queue for (int i = 0; i < edgeTo.length; i++) { if (edgeTo[i]!=null){ allEdges.offer(edgeTo[i]); } } return allEdges; } }
Test class
package graph.tu; import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.Queue; public class PrimMSTTest { public static void main(String[] args) throws Exception{ //Prepare a weighted undirected graph BufferedReader br = new BufferedReader(new InputStreamReader(PrimMSTTest.class.getClassLoader().getResourceAsStream("main/resources/min_create_tree_test.txt"))); int total = Integer.parseInt(br.readLine()); graph.tu.EdgeWeightedGraph G = new graph.tu.EdgeWeightedGraph(total); int edgeNumbers = Integer.parseInt(br.readLine()); for (int e = 1;e<=edgeNumbers;e++){ String line = br.readLine();//4 5 0.35 String[] strs = line.split(" "); int v = Integer.parseInt(strs[0]); int w = Integer.parseInt(strs[1]); double weight = Double.parseDouble(strs[2]); //Building weighted undirected edges Edge edge = new Edge(v, w, weight); G.addEdge(edge); } //Create a PrimMST object and calculate the minimum spanning tree in the weighted undirected graph graph.tu.PrimMST primMST = new graph.tu.PrimMST(G); //Gets all edges in the minimum spanning tree Queue<Edge> edges = primMST.edges(); //Traverse and print all edges for (Edge e : edges) { int v = e.either(); int w = e.other(v); double weight = e.weight(); System.out.println(v+"-"+w+" :: "+weight); } } }
kruskal Algorithm
Main ideas:
The edges are processed according to their weights (from small to large), and the edges are added to the minimum spanning tree
- kruskal algorithm is another algorithm to calculate the minimum spanning tree of a weighted undirected graph. Its main idea is to deal with them according to the weight of edges (from small to large). The edges are added to the minimum spanning tree. The added edges will not form a ring with the edges that have been added to the minimum spanning tree until the tree contains V-1 edges.
Differences between kruskal algorithm and prim algorithm:
- Prim algorithm constructs the minimum spanning tree one by one, and adds an edge to a tree at each step.
- kruskal algorithm constructs the minimum spanning tree side by side, but its segmentation rules are different. Every time it looks for the edge, it will connect two trees in a forest. If a weighted undirected graph is composed of V vertices, and each vertex forms an independent tree under initialization, then the V vertices correspond to V trees to form a forest. kruskal algorithm will merge the two trees into one tree every time until there is only one tree left in the whole forest.
API design
Implementation principle of kruskal algorithm
- When designing the API, a MinPriorityQueue pq is used to store all edges in the graph. Each time pq.delMin() is used to take out the edge with the smallest weight and obtain the two vertices V and W associated with the edge. uf.connect(v,w) is used to judge whether V and W are connected. If connected, it is proved that the two vertices are in the same tree, so this edge can no longer be added to the minimum spanning tree, Because adding an edge to any two vertices of a tree will form a ring, and the minimum spanning tree cannot have a ring. If it is not connected, the tree where vertex v is located and the tree where vertex W is located will be merged into a tree through uf.connect(v,w), and the edge will be added to the mst queue. In this way, if all edges are processed, Finally, all the edges of the minimum spanning tree are stored in mst.
Auxiliary class
1.Edge - edge
2.EdgeWeightedGraph - weighted undirected graph
3.UF_Tree_Weighted -- union search set
package graph.tu; public class UF_Tree_Weighted { //Record the node element and the identification of the group in which the element is located private int[] eleAndGroup; //Record and check the number of data groups in the set private int count; //It is used to store the number of nodes saved in the tree corresponding to each root node private int[] sz; //Initialize and query set public UF_Tree_Weighted(int N){ //The number of initialization packets. By default, there are N packets this.count = N; //Initialize eleAndGroup array this.eleAndGroup = new int[N]; //Initialize the element in eleAndGroup and the identifier of its group, make the index of eleAndGroup array as the element of each node of the query set, and make the value at each index (the identifier of the group in which the element is located) the index for (int i = 0; i < eleAndGroup.length; i++) { eleAndGroup[i] = i; } this.sz = new int[N]; //By default, the value at each index in sz is 1 for (int i = 0; i < sz.length; i++) { sz[i] = 1; } } //How many groups are there in the current query set public int count(){ return count; } //Judge and check whether element p and element q in the set are in the same group public boolean connected(int p,int q){ return find(p) == find(q); } //Identifier of the group in which the element p is located public int find(int p){ while(true){ if (p == eleAndGroup[p]){ return p; } p = eleAndGroup[p]; } } //Merge the group of p element and the group of q element public void union(int p,int q){ //Find the root node of the tree corresponding to the group of p and q elements int pRoot = find(p); int qRoot = find(q); //If p and q are already in the same group, there is no need to merge if (pRoot==qRoot){ return; } //To determine whether the tree size corresponding to proot or qroot is large, you need to merge the smaller trees into the larger trees if (sz[pRoot]<sz[qRoot]){ eleAndGroup[pRoot] = qRoot; sz[qRoot]+=sz[pRoot]; }else{ eleAndGroup[qRoot] = pRoot; sz[pRoot]+= sz[qRoot]; } //Number of groups - 1 this.count--; } }
kruskal algorithm - KruskalMST
- PriorityQueue—java.util
package graph.tu; import java.util.PriorityQueue; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedDeque; public class KruskalMST { //Save all edges of the minimum spanning tree private Queue<Edge> mst; //The index represents the vertex. Use uf.connect(v,w) to judge whether vertex v and vertex W are in the same tree. Use uf.union(v,w) to merge the tree where vertex v and vertex W are located private UF_Tree_Weighted uf; //All edges in the graph are stored, and the edges are sorted by weight using the minimum priority queue private Queue<Edge> pq; //According to a weighted undirected graph, the minimum spanning tree calculation object is created public KruskalMST(EdgeWeightedGraph G) { //Initialize mst this.mst = new ConcurrentLinkedDeque<>(); //Initialize uf this.uf = new UF_Tree_Weighted(G.V()); //Initialize pq this.pq = new PriorityQueue<>(G.E()+1); //Store all the edges in the graph in pq for (Edge e : G.edges()) { pq.offer(e); } //Traverse the pq queue, get the edge with the minimum weight, and process it while(!pq.isEmpty() && mst.size()<G.V()-1){ //Find the edge with the least weight Edge e = pq.poll(); //Find the two vertices of the edge int v = e.either(); int w = e.other(v); //Judge whether the two vertices are already in the same tree. If they are in the same tree, the edge will not be processed. If they are not in the same tree, the two trees to which the two vertices belong will be merged into one tree if (uf.connected(v,w)){ continue; } uf.union(v,w); //Get edge e into mst queue mst.offer(e); } } //Gets all edges of the minimum spanning tree public Queue<Edge> edges() { return mst; } }