[데이터 구조] 그래프의 최소 스패닝 트리(Prim 알고리즘, Kruskal 알고리즘)(C 언어)


연결된 그래프의 스패닝 트리는 그래프의 모든 정점을 포함하지만 트리를 형성하기에 충분한 n-1개의 가장자리만 포함하는 최소 연결 하위 그래프입니다. 연결된 네트워크의 모든 스패닝 트리 중에서 사이드 비용의 합이 가장 작은 스패닝 트리 연결된 네트워크의 최소 비용 스패닝 트리(MST) 또는 줄여서 최소 라고 합니다 . 연결된 네트워크의 최소 비용 스패닝 트리는 Prim 알고리즘 Kruskal 알고리즘을 사용
하여 생성할 수 있습니다.

1. 프라임 알고리즘

1.1 구현 단계

기본 단계 :
N=(V,{E})가 연결된 네트워크라고 가정하면 TE는 최소 비용 스패닝 트리의 에지 집합입니다.
① 초기 U={u0}(u0∈V), TE=공집합
② 모든 u∈U, v∈VU 에지 중에서 비용이 가장 적은 에지(u0, v0)를 선택하여 집합 TE에 병합, 그리고 동시에 v0을 병합합니다. U를 입력합니다.
③ U=V가 될 때까지 ②를 반복합니다.
즉, 특정 정점의 가중치가 가장 작은 에지를 매번 선택한다.
:
프라임 알고리즘 단계

1.2 완전한 구현 코드 + 주석

# include<stdio.h>
# define MAX_VERTEX_NUM 20								//最多顶点个数
# define INFINITY 32768

/*图的邻接矩阵存储结构*/
typedef char VertexData;
typedef struct {
    
    
	VertexData vertex[MAX_VERTEX_NUM];					//顶点向量
	int arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM];			//邻接矩阵
	int vexnum, arcnum;									//图的顶点数和弧数
}AdjMatrix;

AdjMatrix G;

/*求顶点位置函数*/
int LocateVertex(AdjMatrix* G, VertexData v) {
    
    
	int k;
	for (k = 0; k < G->vexnum; k++) {
    
    
		if (G->vertex[k] == v)
			break;
	}
	return k;
}

/*创建一个无向网*/
void CreateAdjMatrix(AdjMatrix* G) {
    
    
	int i, j, k, weight;
	VertexData v1, v2;
	printf("请输入图的顶点数和弧数:");
	scanf("%d%d", &G->vexnum, &G->arcnum);				//输入图的顶点数和弧数
	for (i = 0; i < G->vexnum; i++) {
    
    					//初始化邻接
		for (j = 0; j < G->vexnum; j++)
			G->arcs[i][j] = INFINITY;
	}
	printf("请输入图的顶点:");
	for (i = 0; i < G->vexnum; i++)
		scanf(" %c", &G->vertex[i]);					//输入图的顶点
	for (k = 0; k < G->arcnum; k++) {
    
    
		printf("请输入第%d条弧的两个顶点和权值:", k + 1);
		scanf(" %c %c %d", &v1, &v2, &weight);			//输入一条弧的两个顶点和权值
		i = LocateVertex(G, v1);
		j = LocateVertex(G, v2);
		G->arcs[i][j] = G->arcs[j][i] = weight;			//建立对称弧
	}
}

/*普利姆算法*/
void Prime(AdjMatrix G) {
    
    
	int min, i, j, k, mincost = 0;
	int adjvex[MAX_VERTEX_NUM];							//保存相关顶点下标
	int lowcost[MAX_VERTEX_NUM];						//保存相关顶点间边的权值
	lowcost[0] = 0;										//初始化第一个权值为0,即v0加入生成树
	adjvex[0] = 0;										//初始化第一个顶点下标为0
	for (i = 1; i < G.vexnum; i++) {
    
    					//循环除下标为0外的全部顶点
		lowcost[i] = G.arcs[0][i];						//将v0顶点与之有边的权值存入数组
		adjvex[i] = 0;									//初始化都为v0下标
	}
	for (i = 1; i < G.vexnum; i++) {
    
    
		min = INFINITY;									//初始化最小权值为无穷大
		j = 1;
		k = 0;
		while (j < G.vexnum) {
    
    
			if (lowcost[j] != 0 && lowcost[j] < min) {
    
    	//如果权值不为0,且权值小于min
				min = lowcost[j];						//则让当前权值成为最小值
				k = j;									//将当前最小值的下标存入k
			}
			j++;
		}
		printf("(%c,%c)", G.vertex[adjvex[k]], G.vertex[k]);	//打印当前顶点边中权值最小边
		lowcost[k] = 0;									//将当前顶点的权值设置为0,表示此顶点已经完成任务
		mincost += min;

		for (j = 1; j < G.vexnum; j++) {
    
    				//循环所有顶点
			if (lowcost[j] != 0 && G.arcs[k][j] < lowcost[j]) {
    
    
				lowcost[j] = G.arcs[k][j];
				adjvex[j] = k;							//将下标为k的顶点存入adjvex
			}
		}
	}
	printf("\n最小代价为:");
	printf("%d\n", mincost);
}

int main() {
    
    
	CreateAdjMatrix(&G);
	printf("\n最小代价生成树为:");
	Prime(G);
	return 0;
}

1.3 실행 결과

실행 결과 1

2. 크루스칼 알고리즘

2.1 구현 단계

기본 단계 :
N=(V,{E})가 연결된 네트워크라고 가정하고 가중치가 작은 것부터 큰 것 순으로 N의 에지를 정렬합니다.
① n 정점을 n 세트로 취급합니다.
② 가중치가 작은 것부터 큰 것 순으로 에지를 선택하고, 선택한 에지는 두 정점이 같은 정점 집합 에 속하지 않는 것을 만족하고 스패닝 트리 에지 집합 에 넣고 동시에 두 정점이 만나는 정점 모서리의 꼭지점은 병합된 컬렉션에 있습니다.
③ 모든 정점이 같은 정점 집합에 속할 때까지 ②를 반복합니다.
:
Kruskal 알고리즘 단계① 가중치의 오름차순으로 선택하고자 하는 모서리를 정렬하여 (B,C), 5; (B,D), 6; (C,D), 6; (B,F), 11; (D,F), 14, (A,B), 16, (D,E), 18, (A,E), 19, (A,F), 21, (E,F), 33.
정점 수집 상태: {A}, {B}, {C}, {D}, {E}, {F}.
최소 스패닝 트리의 에지 세트: { }.
② 선택하고자 하는 에지 중에서 가중치가 가장 작은 에지를 선택한다: (B,C), 5.
정점 세트 상태는 {A}, {B,C}, {D}, {E}, {F}가 됩니다.
최소 스패닝 트리의 에지 세트: {(B,C)}.
③ 선택하고자 하는 에지 중에서 가중치가 가장 작은 에지를 선택한다: (B,D), 6.
정점 세트 상태는 {A}, {B,C,D}, {E}, {F}가 됩니다.
최소 신장 트리의 가장자리 집합: {(B,C), (B,D)}.
④ 선택할 에지(C, D), 6 중 가중치가 가장 작은 에지를 선택합니다. C, D는 동일한 정점 집합 {B, C, D}에 있으므로 포기하고 a를 다시 선택합니다. 가장 작은 변을 선택할 가장자리의 가중치: (B,F), 11.
정점 세트 상태는 {A}, {B,C,D,F}, {E}가 됩니다.
최소 신장 트리의 가장자리 집합: {(B,C), (B,D), (B,F)}.
⑤ 선택할 에지 중에서 가중치가 가장 작은 에지를 선택: (D, F), 14, D, F는 동일한 정점 집합 {B, C, D, F}에 있으므로 포기하고 다시 선택 선택할 가장자리 중 가중치가 가장 작은 가장자리: (A,B), 16.
정점 집합의 상태는 {A,B,C,D,F}, {E}가 됩니다.
최소 신장 트리의 가장자리 집합: {(B,C), (B,D), (B,F), (A,B)}.
⑥ 선택하고자 하는 Edge 중 가중치가 가장 작은 Edge를 선택한다: (D, E), 18.
정점 세트 상태는 {A,B,C,D,F,E}가 됩니다.
최소 신장 트리의 가장자리 집합: {(B,C), (B,D), (B,F), (A,B), (D,E)}.

지금까지 모든 꼭지점은 동일한 꼭짓점 집합 {A, B, C, D, F, E}에 있고 알고리즘이 종료되고 최소 스패닝 트리가 구성되며 최소 비용은 5+6+11+16+18입니다. =56.

1.2 완전한 구현 코드 + 주석

# include<stdio.h>
# define MAX_VERTEX_NUM 20								//最多顶点个数
# define INFINITY 32768

typedef char VertexData;
/*边存储结构*/
typedef struct Edge {
    
    
	int u;
	int v;
	int weight;
}Edge, EdgeVec[MAX_VERTEX_NUM];

/*图存储结构*/
typedef struct EdgeGraph {
    
    
	VertexData vertex[MAX_VERTEX_NUM];	//顶点数组存储顶点信息
	EdgeVec arcs;						//弧数组存储弧信息
	int vexnum, arcnum;					//顶点数,弧数
}EdgeGraph;

/*求顶点位置函数*/
int LocateVertex(EdgeGraph* G, VertexData v) {
    
    
	int k;
	for (k = 0; k < G->vexnum; k++) {
    
    
		if (G->vertex[k] == v)
			break;
	}
	return k;
}

/*创建一个网*/
void Create(EdgeGraph* G) {
    
    
	int i, j, k, weight;
	VertexData v1, v2;
	printf("请输入图的顶点数和弧数:");
	scanf("%d%d", &G->vexnum, &G->arcnum);			//输入图的顶点数和弧数
	for (i = 0; i < G->vexnum; i++)					//初始化弧的权值
		G->arcs[i].weight = INFINITY;
	printf("请输入图的顶点:");
	for (i = 0; i < G->vexnum; i++)
		scanf(" %c", &G->vertex[i]);				//输入图的顶点
	for (k = 0; k < G->arcnum; k++) {
    
    
		printf("请输入第%d条弧的两个顶点和权值:", k + 1);
		scanf(" %c %c %d", &v1, &v2, &weight);		//输入一条弧的两个顶点和权值
		i = LocateVertex(G, v1);
		j = LocateVertex(G, v2);
		G->arcs[k].u = v1;							//建立弧
		G->arcs[k].v = v2;
		G->arcs[k].weight = weight;
	}
}

/*按权值将边从小到大排序*/
void Sort(EdgeGraph* G) {
    
    
	int i, j;
	Edge temp;
	for (i = 0; i < G->arcnum - 1; i++) {
    
    
		for (j = 0; j < G->arcnum - i - 1; j++) {
    
    
			if (G->arcs[j].weight > G->arcs[j + 1].weight) {
    
    
				temp = G->arcs[j];
				G->arcs[j] = G->arcs[j + 1];
				G->arcs[j + 1] = temp;
			}
		}
	}
}

/*克鲁斯卡尔算法*/
void Kruskal(EdgeGraph G) {
    
    
	int i, mincost = 0;
	Sort(&G);										//对边进行按权值排序
	int Vexset[MAX_VERTEX_NUM];						//Vexset数组用于记录顶点集合,下标对应顶点序号,值对应顶点集合序号
	for (i = 0; i < G.vexnum; i++)					//初始化,各顶点均处于单独的顶点集合中
		Vexset[i] = i;
	int v1, v2, vs1, vs2;
	for (int i = 0; i < G.arcnum - 1; i++){
    
    
		v1 = LocateVertex(&G, G.arcs[i].u);			//弧连接的顶点的序号
		v2 = LocateVertex(&G, G.arcs[i].v);
		vs1 = Vexset[v1];							//顶点所在顶点集合
		vs2 = Vexset[v2];
		if (vs1 != vs2){
    
    							//判断两个顶点不在同一顶点集合中
			printf("(%c,%c)", G.arcs[i].u, G.arcs[i].v);
			mincost += G.arcs[i].weight;
			for (int j = 0; j < G.vexnum; j++){
    
    
				if (Vexset[j] == vs2)				//合并顶点集合
					Vexset[j] = vs1;
			}
		}
	}
	printf("\n最小代价为:");
	printf("%d\n", mincost);
}

int main() {
    
    
	EdgeGraph G;
	Create(&G);
	printf("\n最小代价生成树为:");
	Kruskal(G);
	return 0;
}

1.3 실행 결과

실행 결과 2
참조: Geng Guohua "데이터 구조 - C 언어로 설명(2판)"

더 많은 데이터 구조 내용을 보려면 내 "데이터 구조" 열을 따르 십시오 .

추천

출처blog.csdn.net/weixin_51450101/article/details/122989240