最小生成树之Kruskal算法和Prim算法

依据图的深度优先遍历和广度优先遍历,能够用最少的边连接全部的顶点,并且不会形成回路。

这样的连接全部顶点并且路径唯一的树型结构称为生成树或扩展树。实际中。希望产生的生成树的全部边的权值和最小,称之为最小生成树。

常见的最小生成树算法有Kruskal算法和Prim算法。

Kruskal算法每次选取权值最小的边。然后检查是否增加后形成回路,假设形成回路则须要放弃。终于构成最小生成树。n个顶点的图最小生成树过程例如以下:

边的权值升序排序。

选取全部未遍历的边中权值最小的边,推断增加后是否形成回路,若形成回路,放弃之。又一次从未被遍历的边中选择。

反复上述步骤,直到选中n-1条边。

/*****************Kruskal算法********************/
struct Edge
{
	int v1,v2;//顶点
	int w;//边v1--v2权值
	struct Edge *next;//指向下一条边
}
//h为按边的权值升序排序的单链表
int Kruskal(Edge *h, int *visited)
{
	int edgenum = 0;//记录生成树中边的个数
	int weight = 0;//权值和
	Edge *p = h;
	printf("最小生成树:(顶点1,顶点2,权值)
");

	while(edgenum != maximum)//maximum=顶点数,当边数=顶点数-1时结束
	{
		if(visited[p->v1] == 0 ||visited[p->v2]==0)
			//新增边至少有一个顶点没有被訪问过
		{
			printf("(%d,%d,%d)->",p->v1,p->v2,p->w);
			weight = weight + p->w; //权值和累加
			visited[p->v1] = 1;
			visited[p->v2] = 1;
			edgenum++;//边数+1
		}
		p = p->next;
		if(p==NULL)//无边可增加
		{
			printf("spanning tree fail
");
			break;
		}
	}
	return weight;
}


Prim算法

相比于Kruskal选边生成,Prim算法选择顶点生成最小生成树。

从某个顶点v開始,列出顶点 v 全部邻接点的边 选择权值最小的边(vi-->vj)增加到最小生成树中,并标记该边已被訪问过。
再从vj開始 列出顶点vj全部邻接点的边,从中选择全部未被訪问过的边中权值最小的边 vj-->vk  增加到最小生成树中,并标记该边已被訪问过。

反复上述操作。直到找到n-1条边为止。

以下直接贴代码:

/*******************Prims算法******************/
struct Edge
{
	int v1,v2;//顶点
	int w;//边v1--v2权值
	int marked;//标识该边是否已经被加入到最小生成树中
	struct Edge *next;
}
//h为边节点构成的链表,index表示開始顶点
void Prim(Edge *h,int * visited, int index)
{
	Edge *p,*min;//min每次指向剩余边中中权值最小且   与上一个边共享顶点v1
	int i;
	int edgenum =0;//已连接边数
	int weight =0;//权值
	int vertex;
	min = (Edge*)malloc(sizeof(Edge));
	/***加入第一条边*******/
	min->w = h->w;//最小边权值初值(可随意指定但小于全部边的权值,当然比越大越好)
	p = h;
	while(p!=NULL)
	{
		if(p->v1 ==index && (p->w < min->w))
			min = p; //找到以index开头 且权值最小的边结点
		p = p->next;
	}
	min->marked = 1;//该边已被訪问
	visited[min->v1] = 1;
	visited[min->v2] = 1; //该边两个顶点被訪问过
	edgenum++;
	weight = min->w;
	printf("(%d,%d,%d)->",min->v1,min->v2,min->w);//输出选中边
	/***加入其余边*******/
	while(edgenum != maximum)
	{
		min->w = h->w;
		p = h;
		while(p != NULL)
		{
			if(p->marked==0 && visited[p->v1]+visited[p->v2]==1)//边没有被訪问过 且有且仅仅有一个顶点被訪问,还有一顶点没有被訪问过
				if(p->w < min->w)
					min = p; //找到权值最小的边
			p = p->next;
		}
		min->marked = 1;
		visited[min->v1] =1;
		visited[min->v2] =1;
		edgenum++;
		weight += min->w;
		printf("(%d,%d,%d)->",min->v1,min->v2,min->w);
	}
	printf("
总权值为:%d",weight);
}


原文地址:https://www.cnblogs.com/claireyuancy/p/6748136.html