有向图的强连通分量(最小生成树)

// algo7-2.cpp 实现算法7.9的程序
#include"c1.h"
typedef int VRType;
typedef char InfoType;
#define MAX_NAME 3 // 顶点字符串的最大长度+1
#define MAX_INFO 20 // 相关信息字符串的最大长度+1
typedef char VertexType[MAX_NAME];
#include"c7-1.h"
#include"bo7-1.cpp"
typedef struct
{ // 记录从顶点集U到V-U的代价最小的边的辅助数组定义(见图7.55)
	VertexType adjvex;
	VRType lowcost;
}minside[MAX_VERTEX_NUM];
int minimum(minside SZ,MGraph G)
{ // 求SZ.lowcost的最小正值,并返回其在SZ中的序号
	int i=0,j,k,min;
	while(!SZ[i].lowcost)
		i++;
	min=SZ[i].lowcost; // 第一个不为0的值
	k=i;
	for(j=i+1;j<G.vexnum;j++)
		if(SZ[j].lowcost>0&&min>SZ[j].lowcost) // 找到新的大于0的最小值
		{
			min=SZ[j].lowcost;
			k=j;
		}
		return k;
}
void MiniSpanTree_PRIM(MGraph G,VertexType u)
{ // 用普里姆算法从第u个顶点出发构造网G的最小生成树T,输出T的各条边。算法7.9
	int i,j,k;
	minside closedge;
	k=LocateVex(G,u);
	for(j=0;j<G.vexnum;++j) // 辅助数组初始化
	{
		strcpy(closedge[j].adjvex,u);
		closedge[j].lowcost=G.arcs[k][j].adj;
	}
	closedge[k].lowcost=0; // 初始,U={u}
	printf("最小代价生成树的各条边为\n");
	for(i=1;i<G.vexnum;++i)
	{ // 选择其余G.vexnum-1个顶点
		k=minimum(closedge,G); // 求出T的下一个结点:第k顶点
		printf("(%s-%s)\n",closedge[k].adjvex,G.vexs[k]); // 输出生成树的边
		closedge[k].lowcost=0; // 第k顶点并入U集
		for(j=0;j<G.vexnum;++j)
			if(G.arcs[k][j].adj<closedge[j].lowcost)
			{ // 新顶点并入U集后重新选择最小边
				strcpy(closedge[j].adjvex,G.vexs[k]);
				closedge[j].lowcost=G.arcs[k][j].adj;
			}
	}
}
void main()
{
	MGraph g;
	CreateUDN(g); // 构造无向网g
	Display(g); // 输出无向网g
	MiniSpanTree_PRIM(g,g.vexs[0]); // 用普里姆算法从第1个顶点出发输出g的最小生成树的各条边
}

代码的运行结果 :

请输入无向网G的顶点数,边数,边是否含其它信息(是:1,否:0): 6,10,0
请输入6个顶点的值(<3个字符):
V1 V2 V3 V4 V5 V6
请输入10条边的顶点1 顶点2 权值(以空格作为间隔):
V1 V2 6
V1 V3 1
V1 V4 5
V2 V3 5
V2 V5 3
V3 V4 5
V3 V5 6
V3 V6 4
V4 V6 2
V5 V6 6
6个顶点10条边或弧的无向网。顶点依次是: V1 V2 V3 V4 V5 V6 (见图756)

G.arcs.adj:
32767 6 1 5 32767 32767
6 32767 5 32767 3 32767
1 5 32767 5 6 4
5 32767 5 32767 32767 2
32767 3 6 32767 32767 6
32767 32767 4 2 6 32767
G.arcs.info:
顶点1(弧尾) 顶点2(弧头) 该边或弧的信息:
最小代价生成树的各条边为
(V1-V3)
(V3-V6)
(V6-V4)
(V3-V2)
(V2-V5)


图757 是根据以上程序运行的例子,显示了算法7.9(普里姆算法)求最小生成树的
过程。首先,主程序构造了图756 所示的无向网。然后,调用MiniSpanTree_PRIM(),
由顶点V1 开始,求该网的最小生成树。这样,最小生成树顶点集最初只有V1,其中用到
了辅助数组closedge[]。closedge[i].lowcost 是最小生成树顶点集中的顶点到i 点的最小权
值。若i 点属于最小生成树,则closedge[i].lowcost=0。closedge[i].adjvex 是最小生成树
顶点集中到i 点为最小权值的那个顶点。图757(a)显示了closedge[]的初态。这时最小
生成树顶点集中只有V1,所以closedge[i].adjvex 都是V1,closedge[i].lowcost 是V1 到i
的权值。closedge[0].lowcost=0 , 说明V1 已属于最小生成树顶点集了。在
closedge[].lowcost 中找最小正数,closedge[2].lowcost=1,是最小正数。令k=2,将V3
并入最小生成树的顶点集(令closedge[2].lowcost=0),输出边(V1—V3)。因为V3 到
V2、V5 和V6 的权值小于V1 到它们的权值,故将它们的closedge[].lowcost 替换为V3
到它们的权值;将它们的closedge[].adjvex 替换为V3,如图757(b)所示。重复这个过
程,依次如图757(c)、(d)和(e)所示。最后,closedge[]包含了最小生成树中每一条边
的信息。


// algo7-8.cpp 克鲁斯卡尔算法求无向连通网的最小生成树的程序
#include"c1.h"
typedef int VRType;
typedef char InfoType;
#define MAX_NAME 3 // 顶点字符串的最大长度+1
#define MAX_INFO 20 // 相关信息字符串的最大长度+1
typedef char VertexType[MAX_NAME];
#include"c7-1.h"
#include"bo7-1.cpp"
void kruskal(MGraph G)
{
	int set[MAX_VERTEX_NUM],i,j;
	int k=0,a=0,b=0,min=G.arcs[a][b].adj;
	for(i=0;i<G.vexnum;i++)
		set[i]=i; // 初态,各顶点分别属于各个集合
	printf("最小代价生成树的各条边为\n");
	while(k<G.vexnum-1) // 最小生成树的边数小于顶点数-1
	{ // 寻找最小权值的边
		for(i=0;i<G.vexnum;++i)
			for(j=i+1;j<G.vexnum;++j) // 无向网,只在上三角查找
				if(G.arcs[i][j].adj<min)
				{
					min=G.arcs[i][j].adj; // 最小权值
					a=i; // 边的一个顶点
					b=j; // 边的另一个顶点
				}
				min=G.arcs[a][b].adj=INFINITY; // 删除上三角中该边,下次不再查找
				if(set[a]!=set[b]) // 边的两顶点不属于同一集合
				{
					printf("%s-%s\n",G.vexs[a],G.vexs[b]); // 输出该边
					k++; // 边数+1
					for(i=0;i<G.vexnum;i++)
						if(set[i]==set[b]) // 将顶点b所在集合并入顶点a集合中
							set[i]=set[a];
				}
	}
}
void main()
{
	MGraph g;
	CreateUDN(g); // 构造无向网g
	Display(g); // 输出无向网g
	kruskal(g); // 用克鲁斯卡尔算法输出g的最小生成树的各条边
}

代码的运行结果:

请输入无向网G的顶点数,边数,边是否含其它信息(是:1,否:0): 6,10,0
请输入6个顶点的值(<3个字符):
V1 V2 V3 V4 V5 V6
请输入10条边的顶点1 顶点2 权值(以空格作为间隔):
V1 V2 6
V1 V3 1
V1 V4 5
V2 V3 5
V2 V5 3
V3 V4 5
V3 V5 6
V3 V6 4
V4 V6 2
V5 V6 6
6个顶点10条边或弧的无向网。顶点依次是: V1 V2 V3 V4 V5 V6 (见图756)

G.arcs.adj:
32767 6 1 5 32767 32767
6 32767 5 32767 3 32767
1 5 32767 5 6 4
5 32767 5 32767 32767 2
32767 3 6 32767 32767 6
32767 32767 4 2 6 32767
G.arcs.info:
顶点1(弧尾) 顶点2(弧头) 该边或弧的信息:
最小代价生成树的各条边为
(V1-V3)
(V4-V6)
(V2-V5)
(V3-V6)
(V2-V3)

图758 是根据以上程序运行的例子,显示了克鲁斯卡尔算法求最小生成树的过程。
首先,主程序构造了图756 所示的无向网。然后,调用kruskal(),求该网的最小生成
树。其中用到了辅助数组set[]。set[i]表示第i 个顶点所在的集合。设初态set[i]=i,6 个
顶点分属于6 个集合,如图758(a)所示。在邻接矩阵的上三角中找权值最小的边(因为
是无向网),边(V1—V3)的权值最小,将V1 和V3 并到1 个集合中。方法是将V3 的集合
set[2]赋值为set[0]( V1 的集合),同时将该边删除(令其上在三角的值为无穷)并输出该
边,如图758(b)所示。用此方法依次将V4 和V6、V2 和V5 分别并到1 个集合中,如
图758(c)、图758(d)所示。这时,邻接矩阵上三角中权值最小的边是(V3—V6),这
两顶点分属于两个集合0 和3。将集合3 合并到集合0 中。方法是把集合3 中的V4、V6
都并到集合0 中,如图758(e)所示。这时在邻接矩阵的上三角中首先找到的权值最小边
是(V1—V4),但它们属于同一个集合(set[0]=set[3]=0),删除该边,继续查找。找到
(V2—V3)是权值最小的边,且它们分属于不同的集合。把V3 所在集合中的顶点都并到
V2 所在集合中,使所有顶点都在集合1 中,如图758(f)所示,最后构成了最小生成
树。程序运行结果和普里姆算法的一样。


需要指出的是,虽然在kruskal()中修改了无向网的邻接矩阵,由于kruskal()的形参不
是引用类型,故在主调函数中并没有改变图的结构。

posted @ 2014-09-09 12:31  meiyouor  阅读(648)  评论(0编辑  收藏  举报