2022-1-27 数据结构 图 最小生成树 prim kruskal

24 篇文章 0 订阅
12 篇文章 0 订阅

最小生成树

生成树的概念

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

最小生成树

在这里插入图片描述
在这里插入图片描述

最小生成树(又名:最小权重生成树)

概念:将给出的所有点连接起来(即从一个点可到任意一个点),且连接路径之和最小的图叫最小生成树。最小生成树属于一种树形结构(树形结构是一种特殊的图),或者说是直链型结构,因为当n个点相连,且路径和最短,那么将它们相连的路一定是n-1条。

可以利用参考一个问题理解最小生成树,有n个村庄,每个村庄之间距离不同,要求村庄之间修路,每一个村庄必须与任意一个村庄联通,如何修路最省钱(修的最短)
在这里插入图片描述

MST

在这里插入图片描述

构造最小生成树:prim 算法

在这里插入图片描述
普利姆算法介绍

求最小生成树,也就是在包含n个顶点的连通图中,找出只有(n-1)条边包含所有n个顶点的连通子图,也就是所谓的极小连通子图

具体过程如下:

(1)设G=(V,E)是连通网,T=(U,D)是最小生成树,V,U是顶点集合,E,D是边的集合

(2)若从顶点u开始构造最小生成树,则从集合V中取出顶点u放入集合U中,标记顶点v的visited[u]=1

(3)若集合U中顶点ui与集合V-U中的顶点vj之间存在边,则寻找这些边中权值最小的边,但不能构成回路,将顶点vj加入集合U中,将边(ui,vj)加入集合D中,标记visited[vj]=1

(4)重复步骤②,直到U与V相等,即所有顶点都被标记为访问过,此时D中有n-1条边

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
using namespace std;
#define n 5
#define MaxNum 10000  /*定义一个最大整数*/

/*定义邻接矩阵类型*/
typedef int adjmatrix[n + 1][n + 1];
typedef struct {
	int fromvex, tovex;                 //生成树的起点和终点
	int weight;                         //边的权重
} Edge;
typedef Edge *EdgeNode;                 //定义生成树的别名
int arcnum;     /*边的个数*/

/*建立图的邻接矩阵*/
void CreatMatrix(adjmatrix GA) {
	int i, j, k, e;
	printf("=============================\n");
	printf("图中有%d个顶点\n", n);
	for (i = 1; i <= n; i++) {
		for (j = 1; j <= n; j++) {
			if (i == j) {
				GA[i][j] = 0;         /*对角线的值置为0*/
			}
			else {
				GA[i][j] = MaxNum;    /*其他位置的值置初始化为一个最大整数*/
			}
		}
	}
	printf("请输入边的个数:\n");
	scanf("%d", &arcnum);
	printf("请输入边的信息,依照起点,终点,权值的形式输入:\n");
	for (k = 1; k <= arcnum; k++) {
		std::cout << "please input edge "<<k<<" : " ;
		scanf("%d,%d,%d", &i, &j, &e);  /*读入边的信息如: 1,2,3 回车   表示1和2顶点之间的边权值为3*/
		GA[i][j] = e;
		GA[j][i] = e;
	}
}

/*初始化图的边集数组*/
void InitEdge(EdgeNode GE, int m) {
	int i;
	for (i = 1; i <= m; i++) {
		GE[i].weight = 0;
	}
}

/*依据图的邻接矩阵生成图的边集数组*/
void GetEdgeSet(adjmatrix GA, EdgeNode GE) {
	int i, j, k = 1;
	for (i = 1; i <= n; i++) {
		for (j = i + 1; j <= n; j++) {
			if (GA[i][j] != 0 && GA[i][j] != MaxNum) //邻接矩阵对角线为零,未连接顶点间权值是maxnum,之外是边权值
			{ 
				GE[k].fromvex = i;
				GE[k].tovex = j;
				GE[k].weight = GA[i][j]; //边权值赋给边节点
				k++;
			}
		}
	}
}

/*按升序排列图的边集数组*/
void SortEdge(EdgeNode GE, int m) {
	int i, j, k;
	Edge temp;
	for (i = 1; i < m; i++) {
		k = i;
		for (j = i + 1; j <= m; j++) {
			if (GE[k].weight > GE[j].weight) {
				k = j; // k是最大权值的边序号
			}
		}
		if (k != i) { //k改变过,则将最大权值边原序号为k的移动到前面的第i个位置上
			temp = GE[i];
			GE[i] = GE[k];
			GE[k] = temp;
		}
	}
}

/*利用普里姆算法从初始点v出发求邻接矩阵表示的图的最小生成树*/
void Prim(adjmatrix GA, EdgeNode T) {
	int i, j, k, min, u, m, w;
	Edge temp;
	/*给T赋初值。相应为v1依次到其余各顶点的边*/
	// 最小生成树里面含有顶点数-1条边,即可把所有顶点包括在内
	k = 1;
	cout << "*************  prim **************" << endl;
	for (i = 2; i <= n; i++) {
		if (i != 1) {
			
			T[k].fromvex = 1;
			T[k].tovex = i;
			T[k].weight = GA[1][i]; //和顶点1不连接的顶点间weight = maxnum
			cout  << "初始化 T[" << k << "] : " << ".fromvex " << T[k].fromvex << " .tovex " << T[k].tovex << " .weight " << T[k].weight << endl;	
			k++;
		}
	}
	/*进行n-1次循环,每次求出最小生成树中的第k条边*/
	//从最小生成树顶点集合 U 和 V-U集合 里的顶点之间的边 里面找最小权值边
	for (k = 1; k < n; k++) {
		cout << "———————————  确定最小生成树的第 " << k << " 条边" << endl;
		min = MaxNum;
		m = k;
		for (j = k; j < n; j++) { //for循环的局部变量j
			if (T[j].weight < min) {
				
				min = T[j].weight;
				m = j; //最小权值边j记为 m 
				cout << "更新顶点 " << T[j].fromvex << " 的最小权值边 序号 "<<j<<" , 权值为:"<< min << endl;
			}
		}
		/*把最短边对调到k-1下标位置*/ 
		// 从m 挪到 k位置
		temp = T[k];
		T[k] = T[m];
		T[m] = temp;
		for (i = 1; i < n; i++) 
		{
			cout << "排序 T[" << i << "] : " << ".fromvex "<<T[i].fromvex <<" .tovex "<< T[i].tovex << " .weight "<<T[i].weight<< endl;
		}
		/*把新增加最小生成树T中的顶点序号赋给j*/
		//最小权值边指向的顶点作为新的起始顶点,找它出发权值最小的下一条边
		j = T[k].tovex;

		/*改动有关边,使T中到T外的每个顶点保持一条到眼下为止最短的边*/
		for (i = k + 1; i < n; i++) {
			u = T[i].tovex;
			w = GA[j][u];//从顶点j出发的边
			if (w < T[i].weight) {
				cout << "更新 新顶点 "<<j<<" 与顶点 " << u << "之间的边 的权值: " << w << endl;
				T[i].weight = w;
				T[i].fromvex = j;
			}
		}
		for (i = 1; i < n; i++)
		{
			cout << "更新 T[" << i << "] : " << ".fromvex " << T[i].fromvex << " .tovex " << T[i].tovex << " .weight " << T[i].weight << endl;
		}
	}
}

/*输出边集数组的每条边*/
void OutEdge(EdgeNode GE, int e) {
	int i;
	printf("依照起点,终点。权值的形式输出的最小生成树为:\n");
	for (i = 1; i < e; i++) {
		printf("%d,%d,%d\n", GE[i].fromvex, GE[i].tovex, GE[i].weight);
	}
	printf("=============================\n");
}

int main() {
	adjmatrix GA;
	Edge GE[n*(n - 1) / 2], T[n];
	CreatMatrix(GA);
	//InitEdge(GE, arcnum);
	//GetEdgeSet(GA, GE);
	//SortEdge(GE, arcnum);
	//OutEdge(GE, arcnum+1);
	Prim(GA, T);
	printf("\n");
	OutEdge(T, n);
	return 0;
}

构造最小生成树:kruskal 算法

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值