Prim(普里姆)算法求最小生成树(邻接矩阵)

Prim(普里姆)算法求最小生成树(邻接矩阵)_第1张图片

一、项目功能、方案选择及性能描述:

功能:
1.列出目标系统具有的各个功能,并简述其作用
建立无向图:
输入点的数目和标识、边的数目及其权值,使用二维数组的方式建立无向图并用邻接矩阵的方式进行存储。

找出最小生成树:
从输入的起始点开始,使用Prim算法来寻找最小生成树,并输出树的结点和边。

构造辅助数组:
设置一个一维数组每寻找一个顶点,清空一次,得到最小权值的顶点下标。

画出连通图:
使用EasyX画出之前输入的无向图及标记边的权值。

演示Prim算法:
逐步演示Prim算法的寻找过程,并把得到的最小生成树用蓝色进行标记,辅以文字说明。

画出最小生成树:
对最小生成树进行重构得到MST。

2.列出目标系统计划完成的界面样式及操作
方案选择:
1、完成目标系统计划采用数据结构的逻辑结构;
数据结构:无向图——使用邻接矩阵来存储无向图。
逻辑结构:图形结构——因为图形结构的元素之间的关系是邻接的。

2、完成目标系统计划采用的存储结构。
存储结构:顺序存储结构——使用二维数组来存储邻接矩阵。
Prim(普里姆)算法求最小生成树(邻接矩阵)_第2张图片
性能:
1、说明目标系统的数据处理能力。
一次只能处理一个无向图,无向图使用邻接矩阵来存储,用二维数组来实现,即无向图的顶点数目不超过10000。

2、说明目标系统的时间特性。
使用Prim算法在邻接矩阵中求最小生成树,设顶点数为n,时间复杂度为O(n²)。
响应时间:小于1ms;
更新处理时间:大约1μs;
数据的转换和传送时间:小于1μs;
运行时间:大约1μs。
Prim(普里姆)算法求最小生成树(邻接矩阵)_第3张图片

二、主要开发思路、工作重点及技术路线

主要开发思路:
将项目分为三大模块:无向图模块、Prim算法模块、演示动画模块。

(1)无向图模块:
通过顶点的个数和边及其权值,使用二维数组来构造邻接矩阵,使用邻接矩阵来存储无向图,包括了顶点和边的信息的输入。

(2)Prim算法模块:
通过无向图模块得到一个邻接矩阵,使用Prim算法在邻接矩阵中找到最小生产数,并输出数的边及其两个端点,为动画演示求得结果。

(3)动画演示模块:
由邻接矩阵来画出无向图,通过Prim算法的每一步求得符合条件的顶点及边,并将其输出在画面,一步一步直到寻找到整颗完整的最小生成树。

工作重点:
Prim算法:单独使用一个数组,来存储最小权值边的顶点,来一步步求出最小生成树。
动画演示:在无向图中,根据Prim算法来一步步演示求得的顶点和边,最后得到最小生成树。

技术路线:如采用C/S模式,使用C++语言编程实现。主要涉及的技术有:MFC中各种控件,图形绘制,窗口拆分,自定义消息,线程同步,数据共享锁等。

使用C++语言编程实现。
主要涉及的技术有:EasyX的图片读取、窗口定义、图形绘制等;多函数的互相调用,宏定义等。

开发环境及工具:
系统核心算法采用Visual Studio开发平台,界面采用EasyX来进行动画演示。

三、软件结构设计

1、软件功能结构
Prim(普里姆)算法求最小生成树(邻接矩阵)_第4张图片
2、功能模块说明
2.1 构造无向图
模块全名:void CreateUDN(MGraph *G);
初始条件:MGraph、ArcCell两个结构体为空,无向图的大小未定义。
过程:输入点的数目和标识、边的数目及其权值,使用二维数组的方式建立无向图并用邻接矩阵的方式进行存储。
操作结果:定义无向图的顶点数和边数,并用邻接矩阵存储了无向图的顶点及其边的权值。

2.2 构造辅助数组:
模块全名:int minimun(MGraph G, closedge close);
初始条件:closedge a_close为空
过程:设置一个一维数组每寻找一个顶点,清空一次,得到最小权值的顶点下标。
操作结果: closedge a_close每寻找一个顶点,清空一次,得到最小权值的顶点下标

2.3 PRIM算法求最小生成树:
模块全名:void miniSpanTreePrim(MGraph G, VertexType u);
初始条件:已知起始顶点的下标,closedge a_close为空。
过程:从输入的起始点开始,使用Prim算法来寻找最小生成树,并输出树的结点和边。
操作结果:每一次得到一个最小权值边的终止顶点的下标。

2.4 画出连通图:
模块全名:void cartoon_DRAW();
初始条件:画布为空,背景为白,画线颜色为黑色。
过程:使用EasyX画出之前输入的无向图及标记边的权值。
操作结果:画出之前输入的无向图及标记边的权值。

2.5演示PRIM算法:
模块全名:void cartoon_TEXT();
初始条件:背景为白,画线颜色为蓝色,画布有无向图一个。
过程:逐步演示Prim算法的寻找过程,并把得到的最小生成树用蓝色进行标记,辅以文字说明。
操作结果:逐步演示Prim算法的寻找过程,并把得到的最小生成树用蓝色进行标记,辅以文字说明。

2.6画出最小生成树:
模块全名:void cartoon_MST();
初始条件:得到了无向图及其最小生成树权值的边和过程的文字说明。
过程:对最小生成树进行重构得到MST。
操作结果:对最小生成树进行重构得到MST。

四、数据结构设计

1、构造无向图:
数组:使用二维数组可以存储邻接矩阵,无向图可以用邻接矩阵表示,且邻接矩阵在Prim算法中寻找更加方便,可以节约空间。
定义:

typedef struct {
     
ARCType adj;                            //对于无权图,用 1 或 0 表示是否相邻;对于带权图,直接为权值
}ArcCell, AdjMatrix[MAX_SIZE][MAX_SIZE];
typedef struct {
     
	VertexType vexs[MAX_SIZE];				//存储图中顶点数据
	AdjMatrix arcs;                         //二维数组,记录顶点之间的关系
	int vexnum, arcnum;                     //记录图的顶点数和边数
}MGraph;

2、构造辅助数组:
数组:使用一维数组来存储寻找的顶点的边,使用遍历寻找出权值最小的边,对应的下一个顶点。
定义:

typedef struct {
     
	VertexType adjvex;						//记录权值最小的边的起始点
	ARCType lowcost;						//记录该边的权值
}closedge[MAX_SIZE];

3、PrIM算法求最小生成树:
数组:利用邻接矩阵和辅助数组来求得最小生成树,从起始点开始,寻找出权值最小的边及其及其对应的点,再寻找下一个顶点

五、关键算法设计

1、PRIM算法求最小生成树算法
Prim(普里姆)算法求最小生成树(邻接矩阵)_第5张图片
Prim(普里姆)算法求最小生成树(邻接矩阵)_第6张图片
Prim(普里姆)算法求最小生成树(邻接矩阵)_第7张图片
Prim(普里姆)算法求最小生成树(邻接矩阵)_第8张图片

Prim(普里姆)算法求最小生成树(邻接矩阵)_第9张图片
Prim(普里姆)算法求最小生成树(邻接矩阵)_第10张图片

Prim(普里姆)算法求最小生成树(邻接矩阵)_第11张图片
Prim(普里姆)算法求最小生成树(邻接矩阵)_第12张图片
时间效率分析:由于使用二维数组进行逐行遍历搜索,所以是O(n²)

空间效率分析:使用二维数组和一维辅助数组来实现功能,所以是O(n+n²)

六、构造辅助算法

1、close辅助数组算法
Prim(普里姆)算法求最小生成树(邻接矩阵)_第13张图片
时间效率分析:因为数组是顺序存储,需要进行遍历,所以为O(n)

空间效率分析:因为是一维数组,所以是O(n)

2、构造无向图算法
Prim(普里姆)算法求最小生成树(邻接矩阵)_第14张图片
时间效率分析:因为二维数组是顺序存储,需要进行输入,所以为O(n²)

空间效率分析:因为是二维数组,所以是O(n²)

七、算法实现(源代码)

#include
#include
#include
#include
#include
#include 
#include 
using namespace std;
#define VertexType int
#define ARCType int  
#define INFINITY 65535						//表示权值很大,意味着两个顶点之间没有连接
#define MAX_SIZE 100

typedef struct {
     
	ARCType adj;                            //对于无权图,用 1 或 0 表示是否相邻;对于带权图,直接为权值
}ArcCell, AdjMatrix[MAX_SIZE][MAX_SIZE];
typedef struct {
     
	VertexType vexs[MAX_SIZE];				//存储图中顶点数据
	AdjMatrix arcs;                         //二维数组,记录顶点之间的关系
	int vexnum, arcnum;                     //记录图的顶点数和边数
}MGraph;

//辅助数组,用于每次筛选出权值最小的边的邻接点
typedef struct {
     
	VertexType adjvex;						//记录权值最小的边的起始点
	ARCType lowcost;						//记录该边的权值
}closedge[MAX_SIZE];
closedge a_close;							//创建一个全局数组,因为每个函数中都会使用到

//根据顶点本身数据,判断出顶点在二维数组中的位置
int LocateVex(MGraph G, VertexType v) {
     
	int i = 0;
	//遍历一维数组,找到变量v
	for ( ; i < G.vexnum; i++) {
     
		if (G.vexs[i] == v) {
     
			return i;
		}
	}
	return -1;
}
//构造无向图
void CreateUDN(MGraph *G) {
     
	scanf_s("%d,%d", &(G->vexnum), &(G->arcnum));  //输入图中的顶点数目和边的数目
	for (int i = 0; i < G->vexnum; i++) {
     
		scanf_s("%d", &(G->vexs[i]));		 //输入图中的顶点的标识数字或字母
	}
	for (int i = 0; i < G->vexnum; i++) {
     
		for (int j = 0; j < G->vexnum; j++) {
     
			G->arcs[i][j].adj = INFINITY;
		}
	}
	for (int i = 0; i < G->arcnum; i++) {
     
		int v1, v2, w;
		scanf_s("%d,%d,%d", &v1, &v2, &w);	//输入边的两个端点和边的权值
		int m = LocateVex(*G, v1);
		int n = LocateVex(*G, v2);
		if (m == -1 || n == -1) {
     
			printf("no this vertex\n");
			return ;
		}
		G->arcs[n][m].adj = w;				//将权值存进一条边
		G->arcs[m][n].adj = w;				//因为是无向图,所以边的逆向也是相同的权值
	}
}
//在辅助数组中找出权值最小的边的数组下标,就可以间接找到此边的终点顶点。
int minimun(MGraph G, closedge close) {
     
	int min = INFINITY;
	int min_i = -1;
	for (int i = 0; i < G.vexnum; i++) {
     
		//权值为0,说明顶点已经归入最小生成树中;然后每次和min变量进行比较,最后找出最小的。
		if (close[i].lowcost > 0 && close[i].lowcost < min) {
     
			min = close[i].lowcost;
			min_i = i;
		}
	}
	return min_i;		//返回最小权值所在的数组下标
}

//普里姆算法函数,G为无向网,u为在网中选择的任意顶点作为起始点
void miniSpanTreePrim(MGraph G, VertexType u) {
     
	int k = LocateVex(G, u);			//找到该起始点在顶点数组中的位置下标

	/*首先将与该起始点相关的所有边的信息:边的起始点和权值,存入辅助数组中相应的位置,
	例如(1,2)边,adjvex为0,lowcost为6,存入a_close[1]中,辅助数组的下标表示该边的顶点2*/
	for (int i = 0; i < G.vexnum; i++) {
     
		if (i != k) {
     
			a_close[i].adjvex = k;
			a_close[i].lowcost = G.arcs[k][i].adj;
		}
	}
	a_close[k].lowcost = 0;		//由于起始点已经归为最小生成树,所以辅助数组对应位置的权值为0,这样,遍历时就不会被选中

	//选择下一个点,并更新辅助数组中的信息
	for (int i = 1; i < G.vexnum; i++) {
     
		k = minimun(G, a_close);			//找出权值最小的边所在数组下标
		printf("< v%d v%d >", G.vexs[a_close[k].adjvex], G.vexs[k]);			//输出选择的路径
		a_close[k].lowcost = 0;				//归入最小生成树的顶点的辅助数组中的权值设为0
		
		/*信息辅助数组中存储的信息,由于此时树中新加入了一个顶点,需要判断,
		由此顶点出发,到达其它各顶点的权值是否比之前记录的权值还要小,如果还小,则更新*/
		for (int j = 0; j < G.vexnum; j++) {
     
			if (G.arcs[k][j].adj < a_close[j].lowcost) {
     
				a_close[j].adjvex = k;
				a_close[j].lowcost = G.arcs[k][j].adj;
			}
		}
	}
	printf("\n");
}
//使用EasyX来画出输入的连通图
void cartoon_DRAW() {
     
	setlinecolor(BLACK);				//设置当前画线颜色为黑色
	setfillcolor(WHITE);				//设置当前的填充颜色为白色
	settextstyle(20, 0, _T("宋体"));	//设置字体为20号的宋体
	setlinestyle(PS_ENDCAP_FLAT, 3);	//设置画线粗细为3
	//画出每一个顶点及其标号
	/*easyX部分乱码丢失*/
	//按照输入的邻接矩阵来画线连接各个顶点。得到连通图
	/*easyX部分乱码丢失*/
	settextstyle(20, 0, _T("宋体"));
	outtextxy(870, 260, "连通图");
}
//在画出的连通图上,按照Prim算法来逐步演示最小生成树
void cartoon_TEXT() {
     
	setlinecolor(BLUE);			//设置当前画线颜色为蓝色
	setfillcolor(BLUE);			//设置当前的填充颜色为蓝色
	//Prim算法得到最小生成树的步骤
	system("pause");
	settextstyle(20, 0, _T("宋体"));
	char s1[] = "第1步:将顶点1加入到U中。此时, V - U = { 1,2,3,4,5,6 }。";
	outtextxy(0, 0, s1);
	system("pause");
	/*easyX部分乱码丢失*/
	drawtext(_T("5"), &r5, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
	system("pause");
}
//画出通过Prim算法得到的最小生成树
void cartoon_MST() {
     
	setfillcolor(BLUE);					//设置当前画线颜色为蓝色
	setlinecolor(BLUE);					//设置当前的填充颜色为蓝色
	settextstyle(20, 0, _T("宋体"));	//设置字体为20号的宋体
	setlinestyle(PS_ENDCAP_FLAT, 3);	//设置画线粗细为3
	//逐步画出最小生成树
	/*easyX部分乱码丢失*/
}

int main() {
     
	int num_dh;
	MGraph G;
	CreateUDN(&G);
	miniSpanTreePrim(G, 1);

	system("pause");
	initgraph(1200, 700);		//创建画布
	setbkcolor(WHITE);			//设置画布背景为白色
	cleardevice();
	settextcolor(BLACK);		//设置文字颜色为黑色
	cartoon_DRAW();				//画出连通图
	cartoon_TEXT();				//在连通图上演示Prim算法
	cartoon_MST();				//输出最小生成树
	system("pause");
	closegraph();
	return 0;
}
/*测试样例:
6,10
1
2
3
4
5
6
1,2,6
1,3,1
1,4,5
2,3,4
2,5,3
3,4,5
3,5,6
3,6,4
4,6,2
5,6,6
*/

八、项目测试

输入样例得到:
Prim(普里姆)算法求最小生成树(邻接矩阵)_第15张图片
EasyX演示:
Prim(普里姆)算法求最小生成树(邻接矩阵)_第16张图片
Prim(普里姆)算法求最小生成树(邻接矩阵)_第17张图片
Prim(普里姆)算法求最小生成树(邻接矩阵)_第18张图片

参考文献
[1] 严蔚敏,吴伟民.数据结构[M].北京:清华大学出版社,2007.
[2] [] Stephen Prata 著,张海龙,袁国忠 译.C++ Primer Plus.人民邮电出版社.2012
[3] 算法导论. [] Thomas H.Cormen 著,殷建平,徐云,王刚 译. 机械工业出版社.2013
[4] 深入理解计算机系统. [] 兰德尔 E.布莱恩特(Randal E.·Bryant) 著,龚奕利,贺莲 译. 机械工业出版社. 2016

你可能感兴趣的:(算法,算法,数据结构,c++,矩阵)