最小生成树之prime算法(邻接矩阵实现)

(一)prime算法

【概念】:
***生成树:一个连通图的生成树,指的是该图的一个子图(没有形成回路),他包含图的所有结点,但只有把所有结点连接在一起的N-1条必要的边;
***最小生成树:一个连通图的生成树中,所有边的权值加起来最小的生成树;称为最小生成树;
【简介】:Prime算法可在加权连通图里搜索最小生成树。即:所有边的权值之和为最小。
Prime算法是图论中求最小生成树的一种算法,与之类似的算法还有Kruskal算法;
区别:

  1. Prime算法适合边多定点少的图;
    2.Dijkstra算法适合边少定点多的图;

1.1 存图方式

要求最小生成树,当然首先要把图存进一个东西中,这样才能图对进行搜索操作。
一般的存图方式有三种:
1.邻接矩阵

存图思想:用一个矩阵来记录一个图,矩阵第 i 行第 j 列的值就表示顶点 i 到顶点 j 的权值

1.2 邻接表

存图思想:对每个顶点,使用不定长的链表来存储从该点出发的所有边的情况,第 i 个链表的第 j 个值就是顶点 i 到顶点 j 的权值。

1.3 链式前向星

存图思想:主要的数据结构是边数组(存储边的数组),当然想要完美表示图,光有一个边集数组还不够,还要有一个数组每一个点的第一条边。同时,每一条边都需要存储接下来一条边的“指针”

1.2 Prime算法分解

1.2.1 用到的数组

我写的这个Prime算法存图用的是邻接矩阵。

int mp[N][N]; //使用邻接矩阵存图;
int dis[N]; //到生成树的最短距离;
int vis[N]; //标记数组,标记该结点是否纳入集合,即该结点是否访问过;
int p[N]; //存储父亲结点;

1.2.2 初始化

初始化:自己与自己的距离为0,自己与别的结点的距离初始化为无穷大,即,表示不连通;
将所有的结点都标记为为访问,我这用0表示未访问,1表示已访问;

memset(vis, 0, sizeof(vis)); //全部未访问过
无穷大:#define INF 0x3f3f3f3f

void inin(int n)//初始化:mp数组初始化,vis标记数组初始化;
{
	int i, j;
	memset(vis, 0, sizeof(vis));		//全部未访问过
	for (i = 1; i <= n; i++)
	{
		for (j = 1; j <= n; j++)
		{
			if (i == j)
				mp[i][j] = 0;		//自己到自己的距离为0;
			else
				mp[i][j] = INF;		//任意两点之间的距离置为无穷大,即:刚开始都未连接;
		}
	}
}

1.2.3 Prime算法主体

传入一个节点,当然,传入的这个节点是随意的。将这个点到其他点的距离存入dis数组,并将传入的这个点标记为已访问。
然后找出从st(传入的那个点)出发的路径中的最短的一个路径;并将它到达的那个点记性并标记。然后更新dis数组(重点):如果该结点没有被访问过,且点距离当前点的距离更近,就执行更新;最后dis数组中就是最小生成树的最短路径的集合;对其求和,即是最小生成树的最短路径;
代码实现:

void prime(int st, int n)	//st是任意一个开始的结点,此节点随意;
{
	int i, j;
	for (i = 1; i <= n; i++)	//将与st点相连的路径存到dis数组中;
	{
		if (mp[st][i] != INF)
		{
			p[i] = st;
		}
		dis[i] = mp[st][i];
	}
	vis[st] = 1;	//将该点标记为访问过;
	while (1)
	{
		int k = -1;
		int min = INF;
		for (i = 1; i <= n; i++)	//在此时从st发出的路径中找到一个最短的路径;
		{
			if (vis[i] != 1 && dis[i] < min)
			{
				min = dis[i];
				k = i;	//此时,st到i的路径最短,并将i记下,后面标记为已访问;
			}
		}
		if (k == -1)	//所有结点都已访问;即:已生成最小生成树;跳出死循环;
		{
			break;
		}
		vis[k] = 1;		//将刚才i连接的结点标记为已访问;
		for (i = 1; i <= n; i++)	//更dis数组,
		{
			if (vis[i] != 1 && dis[i] > mp[k][i])	//该结点没有被访问过,且点距离当前点的距离更近,就执行更新
			{
				dis[i] = mp[k][i];
				p[i] = k;
			}
		}
	}
	for (i = 1; i <= n; i++)
	{
		sum = sum + dis[i];
	}
	
}

完整的使用邻接矩阵存图的Prime算法如下:

#include<iostream>
#include<cstring>
using namespace std;
#define N 1000
#define INF 0x3f3f3f3f
int mp[N][N];	//使用邻接矩阵存图;
int dis[N];     //到生成树的最短距离;
int vis[N];		//标记数组,标记该结点是否纳入集合,即该结点是否访问过;
int sum = 0;
int p[N];		//父亲结点;
void inin(int n)//初始化:mp数组初始化,vis标记数组初始化;
{
	int i, j;
	memset(vis, 0, sizeof(vis));		//全部未访问过
	for (i = 1; i <= n; i++)
	{
		for (j = 1; j <= n; j++)
		{
			if (i == j)
				mp[i][j] = 0;		//自己到自己的距离为0;
			else
				mp[i][j] = INF;		//任意两点之间的距离置为无穷大,即:刚开始都未连接;
		}
	}
}
void prime(int st, int n)	//st是任意一个开始的结点,此节点随意;将st看做最小生成树的根节点
{
	int i, j;
	for (i = 1; i <= n; i++)	//将st点出发的所有路径存到dis数组中;
	{
		if (mp[st][i] != INF)
		{
			p[i] = st;
		}
		dis[i] = mp[st][i];
	}
	vis[st] = 1;	//将该点标记为访问过;
	while (1)
	{
		int k = -1;
		int min = INF;
		for (i = 1; i <= n; i++)	//在此时从st发出的路径中找到一个最短的路径;即:在当前的生成树中找一条最短路径;
		{
			if (vis[i] != 1 && dis[i] < min)
			{
				min = dis[i];
				k = i;	//此时,st到i的路径最短,并将i记下,后面标记为已访问;
			}
		}
		if (k == -1)	//所有结点都已访问;即:已生成最小生成树;跳出死循环;
		{
			break;
		}
		vis[k] = 1;		//将刚才i连接的结点标记为已访问;
		for (i = 1; i <= n; i++)	//更dis数组,
		{
			if (vis[i] != 1 && dis[i] > mp[k][i])	//该结点没有被访问过,且该点到K点的距离比该点到根节点的距离更近,就执行更新
			{
				dis[i] = mp[k][i];
				p[i] = k;
			}
		}
	}
	for (i = 1; i <= n; i++)
	{
		sum = sum + dis[i];
	}
	
}
void parent(int n)
{
	int i, j;
	for (i = 1; i <= n; i++)
	{
		if (p[i] == i)
			cout << i << "为根节点" << endl;
		else
			cout << i << "的父亲结点是:" << p[i] << endl;
	}
}
int main()
{
	int n;
	int i, j;
	cin >> n;
	inin(n);
	for (i = 1; i <= n; i++)
	{
		for (j = 1; j <= n; j++)
		{
			cin >> mp[i][j];
		}
	}
	prime(1, n);
	cout << sum << endl;
	parent(n);
	return 0;
}

输入样例:

4
0 4 9 21
4 0 8 17
9 8 0 16
21 17 16 0
在这里插入图片描述

如果传入的结点是1,那么,

刚开始的dis数组如下:
0 0 4 9 21
执行更新之后:
0 0 4 8 16

当然,你也可以在输入的时候指定特定点之间的距离:
例:x到y的距离为sp;

x y sp
1 2 5
1 3 3
1 4 7
1 5 4
1 6 2

    inin(n);
    for (int i=1;i<=m;i++)
    {
        int x,y,sp;
       cin>>x>>y>>sp
     mp[x][y]=mp[y][x]=sp;
    }
    prim();
    parent();
发布了59 篇原创文章 · 获赞 47 · 访问量 5521

猜你喜欢

转载自blog.csdn.net/qq_44755403/article/details/90322531