最小生成树
原文:https://blog.****.net/qq_35644234/article/details/59106779
、什么是最小生成树
现在假设有一个很实际的问题:我们要在n个城市中建立一个通信网络,则连通这n个城市需要布置n-1一条通信线路,这个时候我们需要考虑如何在成本最低的情况下建立这个通信网?
于是我们就可以引入连通图来解决我们遇到的问题,n个城市就是图上的n个顶点,然后,边表示两个城市的通信线路,每条边上的权重就是我们搭建这条线路所需要的成本,所以现在我们有n个顶点的连通网可以建立不同的生成树,每一颗生成树都可以作为一个通信网,当我们构造这个连通网所花的成本最小时,搭建该连通网的生成树,就称为最小生成树。
构造最小生成树有很多算法,但是他们都是利用了最小生成树的同一种性质:MST性质(假设N=(V,{E})是一个连通网,U是顶点集V的一个非空子集,如果(u,v)是一条具有最小权值的边,其中u属于U,v属于V-U,则必定存在一颗包含边(u,v)的最小生成树),下面就介绍两种使用MST性质生成最小生成树的算法:普里姆算法和克鲁斯卡尔算法。
2、普里姆算法—Prim算法
算法思路:
首先就是从图中的一个起点a开始,把a加入U集合,然后,寻找从与a有关联的边中,权重最小的那条边并且该边的终点b在顶点集合:(V-U)中,我们也把b加入到集合U中,并且输出边(a,b)的信息,这样我们的集合U就有:{a,b},然后,我们寻找与a关联和b关联的边中,权重最小的那条边并且该边的终点在集合:(V-U)中,我们把c加入到集合U中,并且输出对应的那条边的信息,这样我们的集合U就有:{a,b,c}这三个元素了,一次类推,直到所有顶点都加入到了集合U。
下面我们对下面这幅图求其最小生成树:
假设我们从顶点v1开始,所以我们可以发现(v1,v3)边的权重最小,所以第一个输出的边就是:v1—v3=1:
然后,我们要从v1和v3作为起点的边中寻找权重最小的边,首先了(v1,v3)已经访问过了,所以我们从其他边中寻找,发现(v3,v6)这条边最小,所以输出边就是:v3—-v6=4
然后,我们要从v1、v3、v6这三个点相关联的边中寻找一条权重最小的边,我们可以发现边(v6,v4)权重最小,所以输出边就是:v6—-v4=2.
然后,我们就从v1、v3、v6、v4这四个顶点相关联的边中寻找权重最小的边,发现边(v3,v2)的权重最小,所以输出边:v3—–v2=5
然后,我们就从v1、v3、v6、v4,v2这2五个顶点相关联的边中寻找权重最小的边,发现边(v2,v5)的权重最小,所以输出边:v2—–v5=3
3、普里姆算法—代码实现
(1)采用的是邻接矩阵的方式存储图,代码如下
#include<iostream>
#include<string>
#include<vector>
using namespace std;
//首先是使用邻接矩阵完成Prim算法
struct Graph {
int vexnum; //顶点个数
int edge; //边的条数
int ** arc; //邻接矩阵
string *information; //记录每个顶点名称
};
//创建图
void createGraph(Graph & g) {
cout << "请输入顶点数:输入边的条数" << endl;
cin >> g.vexnum;
cin >> g.edge; //输入边的条数
g.information = new string[g.vexnum];
g.arc = new int*[g.vexnum];
int i = 0;
//开辟空间的同时,进行名称的初始化
for (i = 0; i < g.vexnum; i++) {
g.arc[i] = new int[g.vexnum];
g.information[i]="v"+ std::to_string(i+1);//对每个顶点进行命名
for (int k = 0; k < g.vexnum; k++) {
g.arc[i][k] = INT_MAX; //初始化我们的邻接矩阵
}
}
cout << "请输入每条边之间的顶点编号(顶点编号从1开始),以及该边的权重:" << endl;
for (i = 0; i < g.edge; i++) {
int start;
int end;
cin >> start; //输入每条边的起点
cin >> end; //输入每条边的终点
int weight;
cin >> weight;
g.arc[start-1][end-1]=weight;//无向图的边是相反的
g.arc[end-1][start-1] = weight;
}
}
//打印图
void print(Graph g) {
int i;
for (i = 0; i < g.vexnum; i++) {
//cout << g.information[i] << " ";
for (int j = 0; j < g.vexnum; j++) {
if (g.arc[i][j] == INT_MAX)
cout << "∞" << " ";
else
cout << g.arc[i][j] << " ";
}
cout << endl;
}
}
//作为记录边的信息,这些边都是达到end的所有边中,权重最小的那个
struct Assis_array {
int start; //边的终点
int end; //边的起点
int weight; //边的权重
};
//进行prim算法实现,使用的邻接矩阵的方法实现。
void Prim(Graph g,int begin) {
//close_edge这个数组记录到达某个顶点的各个边中的权重最大的那个边
Assis_array *close_edge=new Assis_array[g.vexnum];
int j;
//进行close_edge的初始化,更加开始起点进行初始化
for (j = 0; j < g.vexnum; j++) {
if (j != begin - 1) {
close_edge[j].start = begin-1;
close_edge[j].end = j;
close_edge[j].weight = g.arc[begin - 1][j];
}
}
//把起点的close_edge中的值设置为-1,代表已经加入到集合U了
close_edge[begin - 1].weight = -1;
//访问剩下的顶点,并加入依次加入到集合U
for (j = 1; j < g.vexnum; j++) {
int min = INT_MAX;
int k;
int index;
//寻找数组close_edge中权重最小的那个边
for (k = 0; k < g.vexnum; k++) {
if (close_edge[k].weight != -1) {
if (close_edge[k].weight < min) {
min = close_edge[k].weight;
index = k;
}
}
}
//将权重最小的那条边的终点也加入到集合U
close_edge[index].weight = -1;
//输出对应的边的信息
cout << g.information[close_edge[index].start]
<< "-----"
<< g.information[close_edge[index].end]
<< "="
<<g.arc[close_edge[index].start][close_edge[index].end]
<<endl;
//更新我们的close_edge数组。
for (k = 0; k < g.vexnum; k++) {
if (g.arc[close_edge[index].end][k] <close_edge[k].weight) {
close_edge[k].weight = g.arc[close_edge[index].end][k];
close_edge[k].start = close_edge[index].end;
close_edge[k].end = k;
}
}
}
}
int main()
{
Graph g;
createGraph(g);//基本都是无向网图,所以我们只实现了无向网图
print(g);
Prim(g, 1);
system("pause");
return 0;
}
---------------------
作者:Ouyang_Lianjun
来源:****
原文:https://blog.****.net/qq_35644234/article/details/59106779
版权声明:本文为博主原创文章,转载请附上博文链接!
---------------------
作者:Ouyang_Lianjun
来源:****
原文:https://blog.****.net/qq_35644234/article/details/59106779
版权声明:本文为博主原创文章,转载请附上博文链接!