Minimum spanning tree of graph (implementation and explanation of prim algorithm and kruskal algorithm)

Keywords: C++ Algorithm data structure

catalogue

  1. Topic introduction

  Let's start with the topic  , It is convenient to understand and reflect the algorithm more intuitively

2. Introduce my understanding of the implementation of prim algorithm code in books

1. Function of lowcast array

2. Function of adjvex array

3.kruskal algorithm

3. If you want the source code, look directly here

  1. Topic introduction

After finishing, I lost several hairs. It is estimated that nearly 200 lines of code were written. As a result, I submitted it to the OJ system and said that it was not completely right. I really can't see what went wrong with such a long code

Come and help me find out what's wrong, woo woo (the example given in the title can be output without error. I don't know where there's still a problem...)

  Let's start with the topic  , It is convenient to understand and reflect the algorithm more intuitively

Title Description

Creates an undirected net based on input. Prim algorithm and Kruskal algorithm are used to construct the minimum spanning tree. (assume that the minimum spanning tree of input data is unique.)

Input:

Number of vertices n

n vertices

Number of sides m

m edge information, format: vertex 1 Vertex 2 weight

Starting point of Prim algorithm v

Output:

Output the sum of the weights of the minimum spanning tree

For the two algorithms, the edge information is output according to the growth order of the tree (the edge vertices in Kruskal are output in ascending order of the array number)

sample input

6
v1 v2 v3 v4 v5 v6 
10
v1 v2 6
v1 v3 1
v1 v4 5
v2 v3 5
v2 v5 3
v3 v4 5
v3 v5 6 

sample output

15
prim:
v1 v3 1
v3 v6 4
v6 v4 2
v3 v2 5
v2 v5 3
kruskal:
v1 v3 1
v4 v6 2
v2 v5 3
v3 v6 4
v2 v3 5

One topic covers two algorithms, and also completes the construction of adjacency matrix

It's mainly the prim algorithm. It's really hard to understand what's in the books. I really follow my own ideas step by step, and then do it in combination with what's in the books

I can only understand the book (big talk data structure)   Code meaning of

------------------------------------------------------------------------------------------------

2. Introduce my understanding of the implementation of prim algorithm code in books

Let's talk about prim algorithm first, and put the function of prim part

void prim(int** Map, int vnum, int s, int Enum,  string* vertax) {
	edgeifo* primEdge = new edgeifo[vnum - 1]; //It is used to store the boundary information of prim algorithm at the time of output
	int locate = 0;//The primedge above the tag store
	int* lowcost = new int[vnum];//Save all path weights between related vertices
	int* adjvex = new int[vnum];//Save the vertex corresponding to the weight of the relevant path
	int sum = 0, cal = 0;
	int row = s;
	for (int i = 0; i < vnum; i++)
	{
		lowcost[i] = Map[row][i]; // Assign all path weights of the starting point to lowcast
		adjvex[i] = s;
	}
	while (cal++ < vnum - 1) {
		int min = infinity;
		for (int i = 0; i < vnum; i++)  //Find the column where the minimum value is located
		{
			if (lowcost[i] != 0 && lowcost[i] < min)
			{
				min = lowcost[i];
				row = i;
			}
		}
		sum += min;
		//Store the currently generated information (in fact, it can be output here, but it is affected by the subject requirements)
		primEdge[locate].head = vertax[adjvex[row]];
		primEdge[locate].tail = vertax[row];
		primEdge[locate].power = min;
		locate++;
		//cout <<  vertax[adjvex[row]] << ' '<<vertax[row] <<" " <<min<< endl;
		lowcost[row] = 0;//Indicates that the task has been completed at this point
		for (int i = 0; i < vnum; i++)  // Assign the vertex row data of the minimum value to lowcast
		{
			if (lowcost[i] != 0 && Map[row][i] < lowcost[i]) {
				lowcost[i] = Map[row][i];
				adjvex[i] = row;  //Used to indicate which point the weight of the position comes from
			}
		}
	}
}

1. Function of lowcast array

The general principle of prim algorithm is actually well understood, that is, starting from the current point, list all path weights of the current point (this is equivalent to listing all elements of this row in the adjacency matrix, that is, put them in the lowcast array),

After listing, find the minimum value min, then go to the next point (that is, get a new row), and then get all the path weights of the next point. Note that at this time, the unexplored path of the previous point should be included (I think the code here is very clever)

How to implement the previous step in code,

Every time you go to a new point, that is, to a new row of the adjacency matrix, traverse all the elements of the row, corresponding to the array in lowcast

For each element in the, if the element of the new row corresponding to the new point is larger than the element of the current lowcast, replace it. So

You can keep the path in the original point. Of course, if each element in the new line is larger than the corresponding lowcast element

Small, then all the paths of the original points will be replaced, which is very flexible.

2. Function of adjvex array

I think this array is the most abstract and difficult to understand. I didn't understand it when I knocked it according to the code idea at the beginning

What exactly does adjvex stand for (useful is definitely useful, but I can't figure out what role it plays)

So I wrote a prim algorithm according to my own understanding, and found that I couldn't use the array of adjvex at all. I thought at the beginning

It's so simple. Hee hee, after inputting the sample, I was stupid and didn't understand why I reported an error.

In the following while loop code, you can see that row changes its value every time

  If you can understand the code here, you will know that row will change in the middle because of the change of the minimum value min, that is, at the beginning of each cycle, row refers to the original vertex, and after the for cycle, when the minimum value is found, row will change, that is, the next new vertex to go, Well, I think the row I want to output is the row at the beginning and the row after the update

So I output in this way. As a result, some vertices are wrong. The initial row is often not what I want. If I output in my way, the first point in the fourth line of the following output example should be v4, but the actual output is v3. I can't figure it out

v1 v3 1
v3 v6 4
v6 v4 2
v4 v2 5
v2 v5 3

Later, I thought over my code ideas again before I finally understood the meaning of the adjvex array. This array is used to solve the above problems. According to my above practice, I can only go to black and output continuously. For example, the output vertex of one line must be the input vertex of the next line, but it is not necessarily the case in practice, because I said earlier

Finding the next point does not mean that the original unexplored paths can be removed, and I made this mistake

And don't know it, so adjvex comes in handy at this time hhhh

		for (int i = 0; i < vnum; i++)  // Assign the vertex row data of the minimum value to lowcast
		{
			if (lowcost[i] != 0 && Map[row][i] < lowcost[i]) {
				lowcost[i] = Map[row][i];
				adjvex[i] = row;  //Used to indicate which point the weight of the position comes from
			}
		}

In fact, only here in the prim algorithm needs to assign a value to adjvex. In fact, this is to tell you what the current path is

If the new path is not overwritten, will the current adjvex keep the path

With the information of the original point

Therefore, when outputting data, I only need to make some changes. Originally, I used row as the input vertex at the beginning of the whole big cycle, but now

Change to the vertex corresponding to adjvex, and the subsequent output vertex is also the row after being updated. This is done

Although there are so many BBS above, I still don't think anyone can understand what I'm talking about. I'll try to make it clear when I'm free

However, the prim algorithm does not have many steps, but the code is abstract and difficult to understand. I deduce it according to my own ideas

I think it is very helpful to understand prim algorithm

-----------------------------------------------------------------------------------------------

-------------------------------------------------------

3.kruskal algorithm

kruskal algorithm is relatively intuitive and easy to understand. The code implementation is simpler than prim. My code is also encapsulated with functions.

kruskal arranges the weights of all edges from small to large from a God's perspective, and then selects the path from small to large

The sample can well construct the minimum spanning tree of the graph. It should be noted that a Find function needs to be written to determine whether a loop will be formed and lead

To build is not a minimum spanning tree

3. If you want the source code, look directly here

#include<iostream>
#include<cstring>
using namespace std;
#define infinity 65535;


typedef struct edgeifo
{
	string head;
	string tail;
	int power;
};

int** MakeGraph(int num, int Enum, string* vertax, edgeifo* edge, char Gtype)
{
	int** matrix = new int* [num]; //Open up space 
	for (int i = 0; i < num; i++)
	{
		matrix[i] = new int[num];
		for (int j = 0; j < num; j++)
		{
			if (i == j)
				matrix[i][j] = 0;
			else
				matrix[i][j] = infinity; //Initialize array 
		}
	}
	for (int j = 0; j < Enum; j++)
	{
		int row, column;
		for (int i = 0; i < num; i++)
		{
			if (vertax[i] == edge[j].head)
			{
				row = i;
			}
		}
		for (int i = 0; i < num; i++)
		{
			if (vertax[i] == edge[j].tail)
			{
				column = i;
			}
		}

		matrix[row][column] = edge[j].power;
		if (Gtype == 'U')
			matrix[column][row] = edge[j].power;
	}
	//Output the array 

	return matrix;
}



void prim(int** Map, int vnum, int s, int Enum,  string* vertax) {
	edgeifo* primEdge = new edgeifo[vnum - 1]; //It is used to store the boundary information of prim algorithm at the time of output
	int locate = 0;//The primedge above the tag store
	int* lowcost = new int[vnum];//Save all path weights between related vertices
	int* adjvex = new int[vnum];//Save the vertex corresponding to the weight of the relevant path
	int sum = 0, cal = 0;
	int row = s;
	for (int i = 0; i < vnum; i++)
	{
		lowcost[i] = Map[row][i]; // Assign all path weights of the starting point to lowcast
		adjvex[i] = s;
	}
	while (cal++ < vnum - 1) {
		int min = infinity;
		for (int i = 0; i < vnum; i++)  //Find the column where the minimum value is located
		{
			if (lowcost[i] != 0 && lowcost[i] < min)
			{
				min = lowcost[i];
				row = i;
			}
		}
		sum += min;
		//Store the currently generated information (in fact, it can be output here, but it is affected by the subject requirements)
		primEdge[locate].head = vertax[adjvex[row]];
		primEdge[locate].tail = vertax[row];
		primEdge[locate].power = min;
		locate++;
		//cout <<  vertax[adjvex[row]] << ' '<<vertax[row] <<" " <<min<< endl;
		lowcost[row] = 0;//Indicates that the task has been completed at this point
		for (int i = 0; i < vnum; i++)  // Assign the vertex row data of the minimum value to lowcast
		{
			if (lowcost[i] != 0 && Map[row][i] < lowcost[i]) {
				lowcost[i] = Map[row][i];
				adjvex[i] = row;  //Used to indicate which point the weight of the position comes from
			}
		}
	}
	//output
	cout << sum << endl << "prim:" << endl;
	for (int i = 0; i < vnum - 1; i++)
	{
		cout << primEdge[i].head << " " << primEdge[i].tail << " " << primEdge[i].power << endl;
	}


}

int Find(int* parent,int f) {
	while (parent[f] > 0) {
		f = parent[f];  //Keep looking for points that have not been connected
	}
	return f;
}

void kruskal(int** Map, int vnum, int Enum ,edgeifo* edge,string* vertax) {
	//Get the edge set array first
	cout << "kruskal:" << endl;
	edgeifo* Earr = new edgeifo[Enum];
	memcpy(Earr, edge, sizeof(struct edgeifo)*Enum);//Copy structure array
	for (int i = 0; i < Enum - 1; i++) //Sort by weight from small to large
	{
		for (int j = 0; j < Enum - i -1; j++)
		{
			if (Earr[j].power > Earr[j + 1].power) {
				edgeifo tp;
				tp = Earr[j];
				Earr[j] = Earr[j + 1];
				Earr[j + 1] = tp;
			}
		}

	}

	int* parent = new int[Enum]; //It is used to store boundary information and judge whether a loop is formed
	for (int i = 0; i < Enum; i++)
	{
		parent[i] = 0; //initialization
	}
	for (int i = 0; i < Enum; i++) //Traverse each edge
	{
		int begin, end;
		for (int j = 0; j < vnum; j++)
		{
			if (Earr[i].head == vertax[j])
				begin = j;
			if (Earr[i].tail == vertax[j])
				end = j;
		}
		int m, n;
		n = Find(parent,begin);
		m = Find(parent,end);
		if (n != m)
		{
			parent[n] = m; //Indicates that the nth vertex is connected with the m vertex
			cout << Earr[i].head << ' ' << Earr[i].tail << ' ' << Earr[i].power << endl;
		}
	}

}




void MatrixIfo(int num, char Gtype) {
	int Enum; string* vertax = new string[num];
	//cout << "please enter each vertax :" << endl
	//Input vertex 
	for (int i = 0; i < num; i++)
	{
		cin >> vertax[i];
	}
	//cout << "enter the edge number:" << endl;// Enter the number of edges and information 
	cin >> Enum;
	edgeifo* edge = new edgeifo[Enum];
	for (int i = 0; i < Enum; i++) {
		cin >> edge[i].head;
		cin >> edge[i].tail;
		cin >> edge[i].power;
	}
	string start; int startNum;
	cin >> start;
	for (int i = 0; i < num; i++)
	{
		if (start == vertax[i])
			startNum = i;
	}

	//Boundary information input completed 
	 //Return adjacency matrix
	int** Map = MakeGraph(num, Enum, vertax, edge, Gtype);

	prim(Map, num, startNum, Enum, vertax); //prim algorithm
	kruskal(Map, num, Enum,edge,vertax); //kruskal Algorithm 
}

int main() {

	char Gtype = 'U';//Directly indicate that it is an undirected graph
	int num;
	cin >> num;
	MatrixIfo(num, Gtype);
}

Rookies write questions and don't like welcome spray

Posted by rpanning on Sat, 20 Nov 2021 21:24:35 -0800