图的连通性

图的连通性

  • 1.无向图的生成树
  • 2.普利姆算法
  • 3.克鲁斯卡尔算法

1.无向图的生成树

生成树可以保证连通分量中的全部顶点都是连通,并且边数是最少的。
最小生成树:在生成树中,边的权值之和最小的生成树

2.普利姆算法

普利姆算法是最小生成树不断生长的过程。找顶点
过程:从一个顶点出发(标记出发点被访问过),选择与它邻接点的边中权值最小的边,将这个边的邻接点标记为被访问过,在以邻接点出发,找到个邻接点的边,比较到该点到个顶点的距离,小的且没被访问过的顶点,则更新距离,在从找到最小的边,往复操作。当最终选了n-1条边的时候,循环结束。
【算法实现】

//普利姆算法函数实现
void MSTGraph(const MGraph* G, int v, LowCost lowcost[])
{//从顶点v出发的连通网的最小生成树
	int flag[MAXSITZE] = { 0 };
	flag[v] = 1;
	for (int i = 0; i < G->vexNum; i++)//以v开始初始化lowcoast数组
	{
		lowcost[i].weight = G->arcs[v][i];
		if (lowcost[i].weight == MAXCONST)lowcost[i].adjNodeNo = -1;
		else lowcost[i].adjNodeNo = v;
	}
	int n = 0;
	for (int n = 1; n < G->vexNum; n++)//对n个顶点的连通图只需循环n-1次
	{//找非零和非无穷大权值分量的最小值
		int minCost = MAXCONST;//预定义的符号常量
		int minPos = -1;//minPos表示被选上顶点的编号
		for (int k = 0; k < G->vexNum; k++)//在后选边中选权值最小的边
		{
			if (flag[k] == 0 && lowcost[k].weight < minCost)
			{
				minCost = lowcost[k].weight;
				minPos = k;
			}
		}
		flag[minPos] = 1;//对被选上的顶点做标记
		//对非零的权值分量进行更新
		for (int k = 0; k < G->vexNum; k++)
		{
			if (flag[k] == 0 && G->arcs[minPos][k] < lowcost[k].weight)//对未访问的结点并且权值大的进行更新
				lowcost[k].weight = G->arcs[minPos][k];
		}
	}//end
}

3.克鲁斯卡尔算法

克鲁斯卡尔的核心就是子树的合并。找边的过程。
过程:将每个顶点看成一个树,将边按照权重从小到大排序。接着从小到大开始选边,判断边的两个顶点(x,y)是否属于同一个子树中,是的话就跳过这个边,不是的话就添加这个边,并将x所属的子树根结点编号该成y所属的子树根结点编号。当选取边达到n-1的时候,算法结束。
【算法实现】

//--求最小生树的克鲁斯卡尔算法——加不构成环的最小边--begin
//初始化函数
void init(const MGraph* G, Edge edge[], int Father[])
{//初始化,Father[]存储i的父结点
	int i=0, j=0, k = 0;
	for (int i = 0; i < G->vexNum; i++)//从邻接矩阵中获取边信息,存放到数组edge中
	{
		for (j = 0; j < i; j++)
		{
			if (G->arcs[i][j] != MAXCONST)
			{
				edge[k].a = i; edge[k].b = j;
				edge[k].len = G->arcs[i][j];
				edge[k].flag = 0;
				k++;
			}
		}
	}//得到边信息结束
	for (i = 0; i < G->vexNum; i++)Father[i] = i;//对每一个顶点初始化为根编号
	for (i = 0; i < k - 1; i++)//以边的权重进行从小到大的排序
	{
		for (j = 0; j < k - 1 - i; j++)
		{
			if (edge[j].len > edge[j + 1].len)
			{
				Edge tmp = edge[j];
				edge[j] = edge[j + 1];
				edge[j + 1] = tmp;
			}
		}
	}//冒泡排序结束
}
//找x所在子树根的编号
int GetFather(int x, int Father[])
{//求编号为x的顶点的所在的最大子树的编号
	if (x != Father[x])Father[x] = GetFather(Father[x], Father);
	return Father[x];//返回x所在子树的根结点编号
}
//克鲁斯卡尔算法
void Kruskal(const MGraph* G, Edge edge[], int Father[])
{
	int k = 0;//k为当前边的编号
	int tot = 0;//tot统计最小生成树的边权值总和
	int cnt = 0;//cnt统计进行了几次合并,进行n-1次合并就得到最小生成树
	int x, y;
	while (cnt < G->vexNum - 1)
	{	//n个顶点构成的生成树只有n-1条边
		//查询边(edge[k].a,edge[k],b)的两个顶点所在在子树根的编号
		x = GetFather(edge[k].a, Father);
		y = GetFather(edge[k].b, Father);
		if (x != y)
		{
			Father[x] = y;//合并到一棵生成树中
			printf("(%d,%d)%d\n", edge[k].a, edge[k].b, edge[k].len);
			tot += edge[k].len;
			edge[k].flag = 1;
			cnt++;
		}
		k++;
	}
	printf("Least tree weight is %d\n", tot);
}

你可能感兴趣的:(数据结构,算法,数据结构,图论)