《数据结构(C语言版)第二版》第六章-图(6.6 图的应用——6.6.1 最小生成树)

6.6.1.1普里姆算法(稠密网)

c 复制代码
#include <stdio.h>
#include <stdlib.h>

#define MaxInt 32767  
//此程序中,将无向网中的权值初始化了为0.因此最终不存在的边其权值为0,而非MaxInt

#define MVNum 100

typedef char VerTexType;
typedef int ArcType;

typedef struct AMGraph
{
	VerTexType vexs[MVNum];
	ArcType arcs[MVNum][MVNum];
	int vexnum;
	int arcnum;
}AMGraph;


typedef struct V_UVexNode
{
	VerTexType adjvex;
	ArcType lowcost;
}V_UVexNode;

V_UVexNode closedge[MVNum];  //此声明数组中前面不能加typedef

void CreateAMGraph(AMGraph& G);
int LocateVex(AMGraph G, VerTexType e);
void printAMGraph(AMGraph G);
void MiniSpanTree_Prim(AMGraph G, VerTexType u);
int Min(AMGraph G, V_UVexNode closedge[]);


int main()
{
	AMGraph G = { {0},{0},0,0 };
	int i = 0;

	CreateAMGraph(G);
	printAMGraph(G);

	printf("\n");

	for (i = 0; i < G.vexnum; i++)
	{
		printf("\n从第%d个顶点%c出发,构造采用邻接矩阵表示的图G的最小生成树T:", i + 1, G.vexs[i]);
		MiniSpanTree_Prim(G, G.vexs[i]);
	}

	return 0;
}

//构造邻接矩阵
void CreateAMGraph(AMGraph& G)
{
	printf("请输入总顶点数:");
	scanf_s(" %d", &G.vexnum);

	printf("请输入总边数:");
	scanf_s(" %d", &G.arcnum);

	int i = 0;
	int j = 0;
	int k = 0;
	VerTexType v1 = '\0';
	VerTexType v2 = '\0';
	ArcType w = 0;

	for (i = 0; i < G.vexnum; i++)
	{
		printf("请输入第%d个顶点的值:", i + 1);
		scanf_s(" %c", &G.vexs[i], sizeof(VerTexType));

		for (j = 0; j < G.vexnum; j++)
		{
			G.arcs[i][j] = 0;
		}
	}

	for (k = 0; k < G.arcnum; k++)
	{
		printf("请输入第%d条边的两个顶点:", k + 1);
		scanf_s(" %c %c", &v1, sizeof(VerTexType), &v2, sizeof(VerTexType));
		printf("请输入第%d条边的权重:", k + 1);
		scanf_s(" %d", &w, sizeof(ArcType));

		i = LocateVex(G, v1);
		j = LocateVex(G, v2);

		G.arcs[i][j] = w;
		G.arcs[j][i] = G.arcs[i][j];
	}
}


int LocateVex(AMGraph G, VerTexType e)
{
	int i = 0;
	for (i = 0; i < G.vexnum && G.vexs[i] != e; i++)
	{
		;
	}

	return i;
}

void printAMGraph(AMGraph G)
{
	int i = 0;
	int j = 0;

	printf("\n各顶点为:");
	for (i = 0; i < G.vexnum; i++)
	{
		printf("%c ", G.vexs[i]);
	}

	printf("\n邻接矩阵为:\n");
	for (i = 0; i < G.vexnum; i++)
	{
		for (j = 0; j < G.vexnum; j++)
		{
			if (G.arcs[i][j] == 0)
			{
				printf("%d   ", G.arcs[i][j]);
			}
			else
			{
				printf("%d   ", G.arcs[i][j]);
			}
		}
		printf("\n");
	}
}


//无向网G以邻接矩阵形式存储,从顶点u出发构造G的最小生成树T, 输出T的各条边
void MiniSpanTree_Prim(AMGraph G, VerTexType u)
{
	int k = 0;
	int i = 0;
	int j = 0;
	VerTexType u0 = '\0';
	VerTexType v0 = '\0';

	k = LocateVex(G, u);

	for (j = 0; j < G.vexnum; ++j)
	{
		if (j != k && (G.arcs[k][j] != 0)) 
		// G.arcs[k][j] != 0代表各顶点与起始顶点u之间的边存在时,再将其权值存储到closedge数组中
		{
			closedge[j].adjvex = u;
			closedge[j].lowcost = G.arcs[k][j];
		}
		else if (j == k)  //如果是第k+1个顶点,起始顶点u本身,则将其在closedge数组中相应位置的closedge[k].adjvex置为$
		{
			closedge[k].adjvex = '$';
			closedge[k].lowcost = 0;
		}
		else if(G.arcs[k][j] == 0) //如果各顶点Vj+1与第k+1个顶点,起始顶点u之间的边不存在,则将closedge数组中的closedge[j].adjvex置为#
		{
			closedge[j].adjvex = '#';
			closedge[k].lowcost = 0;
		}
	}

	for (i = 1; i < G.vexnum; ++i)
	{
		k = Min(G, closedge);  //closedge数组中各顶点的下标与G.vexs中各顶点的下标是一一对应的
		u0 = closedge[k].adjvex;
		v0 = G.vexs[k];   //closedge数组中各顶点的下标与G.vexs中各顶点的下标是一一对应的

		printf("\n最小生成树T中第%d条边为:(%c, %c)", i, u0, v0);

		closedge[k].adjvex = '$';
		closedge[k].lowcost = 0;

		for (j = 0; j < G.vexnum; ++j)
		{
			if ((G.arcs[k][j] != 0) && (closedge[j].adjvex != '$') && (G.arcs[k][j] < closedge[j].lowcost))
			//Vk+1在顶点集U中(closedge[k].adjvex 为 '$'),Vj+1还在顶点集V-U中时(closedge[j].adjvex != '$'),且(Vk+1,Vj+1)这条边存在(G.arcs[k][j] != 0), 再将G.arcs[k][j] 和 closedge[j].lowcost相比较
			{
				closedge[j].adjvex = G.vexs[k];
				closedge[j].lowcost = G.arcs[k][j];
			}
			else if ((closedge[j].adjvex == '#') && (G.arcs[k][j] != 0))
			//顶点U中之前所有的顶点都与Vj+1之间不存在边,现在新加入Vk+1与Vj+1之间存在了边,则不用比较权值(closedge[j].lowcost一定为0),直接将将其权值G.arcs[k][j]存入closedge[j].lowcost
			{
				closedge[j].adjvex = G.vexs[k];
				closedge[j].lowcost = G.arcs[k][j];
			}
		}
	}
}

int Min(AMGraph G, V_UVexNode closedge[])
{
	int i = 0;
	int j = 0;
	int k = 0;

	while (closedge[i].lowcost == 0)
	{
		i++;
	}
	//找到V-U中的第一个顶点,跳出

	VerTexType minAdjvex = closedge[i].adjvex;
	ArcType minCost = closedge[i].lowcost;

	j = i;
	/* 谨记此处要记录一下j = i !!! 因为如果整个程序,至运行结束一次也不进入for循环中的if语句的话,
	那么此时的closedge[i].lowcost就是closedge数组中最小的权值,也要用j返回 */

	for (k = i + 1; k < G.vexnum; k++)
	{
		if (closedge[k].lowcost != 0 && minCost > closedge[k].lowcost)
		{
			minAdjvex = closedge[k].adjvex;
			minCost = closedge[k].lowcost;
			j = k;
		}
	}

	return j;
}



c 复制代码
//无向网G以邻接矩阵形式存储,从顶点u出发构造G的最小生成树T, 输出T的各条边
void MiniSpanTree_Prim(AMGraph G, VerTexType u)
{
	int k = 0;
	int i = 0;
	int j = 0;
	VerTexType u0 = '\0';
	VerTexType v0 = '\0';

	printf("\nu = %c", u);
	k = LocateVex(G, u);
	printf("\nk = %d", k);

	for (j = 0; j < G.vexnum; ++j)
	{
		if (j != k && (G.arcs[k][j] != 0)) 
		// G.arcs[k][j] != 0代表各顶点与起始顶点u之间的边存在时,再将其权值存储到closedge数组中
		{
			closedge[j].adjvex = u;
			closedge[j].lowcost = G.arcs[k][j];
		}
		else if (j == k)  //如果是第k+1个顶点,起始顶点u本身,则将其在closedge数组中相应位置的closedge[k].adjvex置为$
		{
			closedge[k].adjvex = '$';
			closedge[k].lowcost = 0;
		}
		else if(G.arcs[k][j] == 0) //如果各顶点Vj+1与第k+1个顶点,起始顶点u之间的边不存在,则将closedge数组中的closedge[j].adjvex置为#
		{
			closedge[j].adjvex = '#';
			closedge[k].lowcost = 0;
		}
	}


	//打印初始化后的closedge数组
	printf("\n\n打印初始化后的closedge数组:");
	for (i = 0; i < G.vexnum; i++)
	{
		printf("\nclosedge[%d].adjvex = %c", i, closedge[i].adjvex);
		printf(",  closedge[%d].lowcost = %d", i, closedge[i].lowcost);
	}


	for (i = 1; i < G.vexnum; ++i)
	{
		k = Min(G, closedge);  //closedge数组中各顶点的下标与G.vexs中各顶点的下标是一一对应的
		printf("\n\n第%d次for循环:k = %d", i, k);

		u0 = closedge[k].adjvex;
		printf("\n第%d次for循环:u0 = %c", i, u0);

		v0 = G.vexs[k];   //closedge数组中各顶点的下标与G.vexs中各顶点的下标是一一对应的
		printf("\n第%d次for循环:v0 = %c", i, v0);

		printf("\n最小生成树T中第%d条边为:(%c, %c)", i, u0, v0);

		closedge[k].adjvex = '$';
		closedge[k].lowcost = 0;
		printf("\n第%d次for循环往U中新加的顶点为: %c", i, G.vexs[k]);

		for (j = 0; j < G.vexnum; ++j)
		{
			if ((G.arcs[k][j] != 0) && (closedge[j].adjvex != '$') && (G.arcs[k][j] < closedge[j].lowcost))
			//Vk+1在顶点集U中(closedge[k].adjvex 为 '$'),Vj+1还在顶点集V-U中时(closedge[j].adjvex != '$'),且(Vk+1,Vj+1)这条边存在(G.arcs[k][j] != 0), 再将G.arcs[k][j] 和 closedge[j].lowcost相比较
			{
				printf("\n更新的closedge数组的下标为:%d", j);
				closedge[j].adjvex = G.vexs[k];
				closedge[j].lowcost = G.arcs[k][j];

				printf("\nclosedge[%d].adjvex = %c", j, closedge[j].adjvex);
				printf(",  closedge[%d].lowcost = %d", j, closedge[j].lowcost);
			}
			else if ((closedge[j].adjvex == '#') && (G.arcs[k][j] != 0))
			{
				closedge[j].adjvex = G.vexs[k];
				closedge[j].lowcost = G.arcs[k][j];
			}
		}


		printf("\n打印第%d次for循环更新后的closedge数组:", i);
		for (j = 0; j < G.vexnum; j++)  //不能用i作为下标了,退出for循环后,i的值会被延续
		{
			printf("\nclosedge[%d].adjvex = %c", j, closedge[j].adjvex);
			printf(",  closedge[%d].lowcost = %d", j, closedge[j].lowcost);
		}
	}
}

int Min(AMGraph G, V_UVexNode closedge[])
{
	int i = 0;
	int j = 0;
	int k = 0;

	while (closedge[i].lowcost == 0)
	{
		i++;
	}
	//找到V-U中的第一个顶点,跳出

	VerTexType minAdjvex = closedge[i].adjvex;
	ArcType minCost = closedge[i].lowcost;

	j = i;
	/* 谨记此处要记录一下j = i !!! 因为如果整个程序,至运行结束一次也不进入for循环中的if语句的话,
	那么此时的closedge[i].lowcost就是closedge数组中最小的权值,也要用j返回 */

	printf("\nminAdjvex = closedge[%d].adjvex = %c", i, closedge[i].adjvex);
	printf("\nminCost = closedge[%d].lowcost = %d", i, closedge[i].lowcost);

	for (k = i + 1; k < G.vexnum; k++)
	{
		printf("\nk = %d", k);

		if (closedge[k].lowcost != 0 && minCost > closedge[k].lowcost)
		{
			minAdjvex = closedge[k].adjvex;
			minCost = closedge[k].lowcost;
			printf("\nif条件中:k = %d", k);
			j = k;
			printf("\nif条件中:j = %d", j);
		}
	}

	printf("\nj = %d", j);
	return j;
}

6.6.1.2克鲁斯卡尔算法(稀疏网)

c 复制代码
#include <stdio.h>
#include <stdlib.h>

#define MVNum 100
#define MANum 200

typedef char VerTexType;
typedef int ArcType;

typedef struct
{
	VerTexType vexs[MVNum];
	ArcType arcs[MVNum][MVNum];
	int vexnum;
	int arcnum;
}AMGraph;


//辅助数组Edge的定义
typedef struct arc
{
	VerTexType Head;
	VerTexType Tail;
	ArcType lowcost;
}arc;

arc Edge[MANum];

//辅助数组Vexset的定义
int Vexset[MVNum];


void CreateAMGraph(AMGraph& G);
int LocateVex(AMGraph G, VerTexType v);
void printAMGraph(AMGraph G);
void MinSpanTree_Kruskal(AMGraph G);
void Quick_Sort(arc Edge[], int low, int high);

int main()
{
	AMGraph G = { {0},{0},0,0 };

	CreateAMGraph(G);
	printAMGraph(G);
	MinSpanTree_Kruskal(G);

	return 0;
}

void CreateAMGraph(AMGraph& G)
{
	printf("请输入总顶点数:");
	scanf_s(" %d", &G.vexnum);

	printf("请输入总边数:");
	scanf_s(" %d", &G.arcnum);

	int i = 0;
	int j = 0;
	int k = 0;
	VerTexType v1 = '\0';
	VerTexType v2 = '\0';
	ArcType w = 0;

	for (i = 0; i < G.vexnum; i++)
	{
		printf("请输入第%d个顶点的值:", i + 1);
		scanf_s(" %c", &G.vexs[i]);

		for (j = 0; j < G.vexnum; j++)
		{
			G.arcs[i][j] = 0;
		}
	}

	for (k = 0; k < G.arcnum; k++)
	{
		printf("请输入第%d条边依附的两个顶点:", k + 1);
		scanf_s(" %c %c", &v1, sizeof(VerTexType), &v2, sizeof(VerTexType));

		printf("请输入第%d条边的权值:",k+1);
		scanf_s(" %d", &w);

		Edge[k].Head = v1;
		Edge[k].Tail = v2;
		Edge[k].lowcost = w;


		i = LocateVex(G, v1);
		j = LocateVex(G, v2);

		G.arcs[i][j] = w;
		G.arcs[j][i] = G.arcs[i][j];
	}
}


int LocateVex(AMGraph G, VerTexType v)
{
	int i = 0;

	for (i = 0; i < G.vexnum && G.vexs[i] != v; i++)
	{
		;
	}

	return i;
}

void printAMGraph(AMGraph G)
{
	int i = 0;
	int j = 0;

	printf("各顶点为:");
	for (i = 0; i < G.vexnum; i++)
	{
		printf("%c ", G.vexs[i]);
	}

	printf("\n邻接矩阵为:\n");
	for (i = 0; i < G.vexnum; i++)
	{
		for (j = 0; j < G.vexnum; j++)
		{
			printf("%d ", G.arcs[i][j]);
		}

		printf("\n");
	}
}


//无向网G以邻接矩阵形式存储,构造G的最小生成树T, 输出T的各条边
void MinSpanTree_Kruskal(AMGraph G)
{
	int i = 0;
	int j = 0;
	int v1 = '\0';
	int v2 = '\0';
	int vs1 = 0;
	int vs2 = 0;
	int edgeCount = 0; //记录已加入MST的边数

	Quick_Sort(Edge,0,G.arcnum -1);

	/* Vexset数组与G.vexs数组中的下标是一一对应的,即图中被标记序号为第i+1个的顶点Vi,其在G.vexs数组中的下标是i.
	那么在Vexset数组中,下标为i处,存储的就是第i+1个的顶点Vi所在的连通分量的信息。*/
	for (i = 0; i < G.vexnum; i++)
	{
		Vexset[i] = i;
	}
	//每个连通分量用序号来命名。Vexset数组中各下标i处的值i,代表的就是图中第i+1个的顶点Vi所在的连通分量。


	for (i = 0; i < G.arcnum; i++)
	{
		v1 = LocateVex(G, Edge[i].Head);
		v2 = LocateVex(G, Edge[i].Tail);

		vs1 = Vexset[v1];
		vs2 = Vexset[v2];

		if (vs1 != vs2)
		{
			edgeCount++;
			printf("\n最小生成树T中第%d条边为:(%c, %c),其权值为:%d", edgeCount,Edge[i].Head, Edge[i].Tail,Edge[i].lowcost);

			for (j = 0; j < G.vexnum; ++j)
			{
				if (Vexset[j] == vs2)
				{
					Vexset[j] = vs1;
				}
			}
		}
	}
}

//运用快速排序法
void Quick_Sort(arc Edge[], int low,int high)
{
	int i = low;
	int j = high;
	arc key_arc = Edge[i];
	int key = Edge[i].lowcost;

	while (i < j)
	{
		while (i < j && Edge[j].lowcost >= key)
		{
			j--;
		}
		Edge[i] = Edge[j];

		while (i < j && Edge[i].lowcost <= key)
		{
			i++;
		}
		Edge[j] = Edge[i];
	}

	Edge[i] = key_arc;


	if (i - 1 > low)
	{
		Quick_Sort(Edge, low, i - 1);
	}

	if (i + 1 < high)
	{
		Quick_Sort(Edge, i+1, high);
	}
}



c 复制代码
//无向网G以邻接矩阵形式存储,构造G的最小生成树T, 输出T的各条边
void MinSpanTree_Kruskal(AMGraph G)
{
	int i = 0;
	int j = 0;
	int v1 = '\0';
	int v2 = '\0';
	int vs1 = 0;
	int vs2 = 0;
	int edgeCount = 0; //记录已加入MST的边数

	printf("\n排序之前Edge数组中的各边顺序为:");
	for (i = 0; i < G.arcnum; i++)
	{
		printf("\n Edge[%d] :(%c, %c),其权值为:%d",i, Edge[i].Head, Edge[i].Tail, Edge[i].lowcost);
	}


	Quick_Sort(Edge,0,G.arcnum -1);

	printf("\n\n排序之后Edge数组中的各边顺序为:");
	for (i = 0; i < G.arcnum; i++)
	{
		printf("\n Edge[%d] :(%c, %c),其权值为:%d", i, Edge[i].Head, Edge[i].Tail, Edge[i].lowcost);
	}

	/* Vexset数组与G.vexs数组中的下标是一一对应的,即图中被标记序号为第i+1个的顶点Vi,其在G.vexs数组中的下标是i.
	那么在Vexset数组中,下标为i处,存储的就是第i+1个的顶点Vi所在的连通分量的信息。*/
	for (i = 0; i < G.vexnum; i++)
	{
		Vexset[i] = i;
	}
	//每个连通分量用序号来命名。Vexset数组中各下标i处的值i,代表的就是图中第i+1个的顶点Vi所在的连通分量。

	printf("\n打印初始化后的辅助数组Vexset:");
	for (i = 0; i < G.vexnum; i++)
	{
		printf("\nVexset[%d] = %d", i, Vexset[i]);
	}

	for (i = 0; i < G.arcnum; i++)
	{
		printf("\n\n正在进行判断的边是:(%c, %c),其权值为:%d", Edge[i].Head, Edge[i].Tail, Edge[i].lowcost);
		v1 = LocateVex(G, Edge[i].Head);
		v2 = LocateVex(G, Edge[i].Tail);

		vs1 = Vexset[v1];
		printf("\n顶点%c所属连通分量的下标为:%d", Edge[i].Head, vs1);

		vs2 = Vexset[v2];
		printf("\n顶点%c所属连通分量的下标为:%d", Edge[i].Tail, vs2);

		if (vs1 != vs2)
		{
			edgeCount++;
			printf("\n最小生成树T中第%d条边为:(%c, %c),其权值为:%d", edgeCount,Edge[i].Head, Edge[i].Tail,Edge[i].lowcost);

			for (j = 0; j < G.vexnum; ++j)
			{
				if (Vexset[j] == vs2)
				{
					Vexset[j] = vs1;
				}
			}

			printf("\n打印更新后的辅助数组Vexset:");
			for (j = 0; j < G.vexnum; j++)  
			//不能用i做下标值,结束该for循环后,i值还会延续
			{
				printf("\nVexset[%d] = %d", j, Vexset[j]);
			}
		}
	}
}
相关推荐
浮生如梦_1 小时前
Halcon基于laws纹理特征的SVM分类
图像处理·人工智能·算法·支持向量机·计算机视觉·分类·视觉检测
XiaoLeisj2 小时前
【JavaEE初阶 — 多线程】单例模式 & 指令重排序问题
java·开发语言·java-ee
励志成为嵌入式工程师3 小时前
c语言简单编程练习9
c语言·开发语言·算法·vim
捕鲸叉3 小时前
创建线程时传递参数给线程
开发语言·c++·算法
A charmer3 小时前
【C++】vector 类深度解析:探索动态数组的奥秘
开发语言·c++·算法
Peter_chq3 小时前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端
wheeldown4 小时前
【数据结构】选择排序
数据结构·算法·排序算法
记录成长java5 小时前
ServletContext,Cookie,HttpSession的使用
java·开发语言·servlet
前端青山5 小时前
Node.js-增强 API 安全性和性能优化
开发语言·前端·javascript·性能优化·前端框架·node.js
hikktn5 小时前
如何在 Rust 中实现内存安全:与 C/C++ 的对比分析
c语言·安全·rust