最小生成树Prim算法和Kruskal算法的C语言实现

最小生成树是这样一棵树,它包含了图中的所有节点,并且使得总的边权值之和最小。显然,一个图存在最小生成树当且仅当该图连通。对于最小生成树的求法,常用的有两种算法。注意:这两种算法对于负边权值的图也成立。

1.Prim算法

该算法和Dijkstra算法很像,采用的思路是广度优先搜索。将节点分成两个集合S和T,S中为已经处理的节点,T中为未处理的节点。每进行一次操作,在最小生成树中添加一条边(u,v),u∈S,v∈T,该边为S中的节点到T中的节点距离最小的那一条边,添加完成后,将节点v加入集合S中。更新与v相邻并且未被访问的节点的距离,重复之前的步骤。与节点v邻接的节点w的权值更新为min{dw,Cvw}。可由反证法证明该算法的正确性。

程序代码如下:

#include 
#define x 10000
const int NumVertex = 7;
int map[NumVertex][NumVertex] =
  //1  2  3  4  5  6  7
{
	x, 2, 4, 1, x, x, x,  //1
	2, x, x, 3, 10, x, x, //2
	4, x, x, 2, x, x, x,  //3
	1, 3, 2, x, 7, 8, 4,  //4
	x, 10, x, 7, x, x, 6, //5
	x, x, 5, 8, x, x, 1,  //6
	x, x, x, 4, 6, 1, x   //7
};

int distance[NumVertex] = { x, x, x, x, x, x, x };//第i个节点到已知节点的最小权值
int path[NumVertex];//导致第i个节点距离变化的最后一个顶点
int Known[NumVertex];//节点访问情况

void Prim(int Begin, int map[][NumVertex])
{
	distance[Begin] = 0;

	for (;;)
	{
		int min = x, position=0;
		for (int i = 0; i=distance[i])
			{
				min = distance[i];
				position = i;
			}
		}                                    //寻找距离最短的节点
		Known[position] = 1;

		if (min == x)
			break;

		for (int i = 0; i < NumVertex; i++)
		{
			if (map[position][i] != x&&Known[i] == 0)
			{
				if (distance[i]>map[position][i])
				{
					distance[i] = map[position][i];
					path[i] = position+1;
				}                           //更新距离

			}

		}
	}
}

void printarray(int a[])
{
	for (int i = 0; i < NumVertex; i++)
	{
		printf("%d ", a[i]);
	}
}

void main()
{
	Prim(0, map);
	printarray(distance);
	printf("\n");
	printarray(path);
	printf("\n");
}

2.Kruskal算法

该算法的思路比较直观,它从图中的权值最小的边开始,依次选择图中的边,然后判断这条变是否可以接受,当可以接受时将它加入生成树中,当不能接受时选择下一条边。当选定边(u,v)时,如果使得树产生圈,则这条边是不能被接受的,如果不构成圈,那么就能将这条变加到树中。将在同一棵子树中的节点划分到同一集合,将边(u,v)加到树中,可以将其视为集合的合并,所以判断该条边是否能够接受的方法可以采用Find/Union操作,当Find(u)!=Find(v)时,(u,v)能够被接受,执行一次Union(u,v)操作即可。

程序代码如下:

#include 
#include 
#define x 10000
const int NumVertex = 7;
const int NumEdge = 12;
int map[NumVertex][NumVertex] =
//  0  1  2  3  4  5  6
{
	x, 2, 4, 1, x, x, x,  //0
	2, x, x, 3, 10, x, x, //1
	4, x, x, 2, x, 5, x,  //2
	1, 3, 2, x, 7, 8, 4,  //3
	x, 10, x, 7, x, x, 6, //4
	x, x, 5, 8, x, x, 1,  //5
	x, x, x, 4, 6, 1, x   //6
};

int hash[NumVertex];//节点所属的集合
int Edge[NumEdge];//按照邻接表的排列顺序给每条边编号,该数组表示图中第i条边的长度
int BeginVertex[NumEdge], EndVertex[NumEdge];//图中边的起始节点和图中边的终止节点
int AcceptedEdge[NumEdge];//每条边被接受的情况,接受为1,不接受为0
int Known[NumEdge];

 int Find(int Vertex)
{
	return hash[Vertex];
}//查找节点所属的集合

void Union(int Vertex1, int Vertex2)
{
	for (int i = 0; i < NumVertex; i++)
	{
		if (hash[i] == hash[Vertex2])
			hash[i] = hash[Vertex1];
	}
}//合并两个节点

void Kruskal(int map[][NumVertex])
{
	int EdgeOrder = 0;

    for (int i = 0; i < NumVertex; i++)
	{
		hash[i] = i + 1;
	}//散列函数,返回节点所属的集合

	for (int i = 0; i < NumVertex; i++)
	{
		for (int j = i+1; j < NumVertex; j++)
		{
			if (map[i][j]Edge[i])
			{

				min = Edge[i];
				position = i;
			}
		}//搜索距离最小的边

		Known[position] = 1;//标记该变已经被访问过

		if (Find(BeginVertex[position]) != Find(EndVertex[position]))
		{
			Union(BeginVertex[position], EndVertex[position]);
			AcceptedEdge[position] = 1;
		}//如果选取的边加到图中没有构成圈(两节点属于不同集合),则该边可以接受,遂把该边加入图中
	}
}

void printarray(int array[])
{
	for (int i = 0; i 


你可能感兴趣的:(最小生成树Prim算法和Kruskal算法的C语言实现)