最小生成树
概念:将给出的所有点连接起来(即从一个点可到任意一个点),且连接路径之和最小的图叫最小生成树。
数据结构:树形结构,或者说是直链型结构,因为当n个点相连,且路径和最短,那么将它们相连的路一定是n-1条
实现思路:将点分为在树中的点与不在树中的点,每次取出树中点的连接的最小路径,且该路径连接的点不在树中,然后将该路径连接的点加入树中,重复并进行路关于图的几个概念定义:
连通图:在无向图中,若任意两个顶点vivi与vjvj都有路径相通,则称该无向图为连通图。
强连通图:在有向图中,若任意两个顶点vivi与vjvj都有路径相通,则称该有向图为强连通图。
连通网:在连通图中,若图的边具有一定的意义,每一条边都对应着一个数,称为权;权代表着连接连个顶点的代价,称这种连通图叫做连通网。
生成树:一个连通图的生成树是指一个连通子图,它含有图中全部n个顶点,但只有足以构成一棵树的n-1条边。一颗有n个顶点的生成树有且仅有n-1条边,如果生成树中再添加一条边,则必定成环。
最小生成树:在连通网的所有生成树中,所有边的代价和最小的生成树,称为最小生成树。 径更新,即松弛,当取出边达到n-1条时,树已建立。
#include<iostream>
using namespace std;
//用邻接矩阵来存储
#define MaxNum 10//最大顶点数
#define MaxInt 32767//无穷大
#define MinInt -1//认为较小
typedef char VerType;//数据类型
typedef int ArcType;//权值类型
//定义图的结构
typedef struct {
VerType ver[MaxNum];//顶点表
ArcType arc[MaxNum][MaxNum];//邻接矩阵
int vernum, arcnum; //点数,边数
}AMGraph;
//定位
int Locate(AMGraph G, VerType v1) {
for(int i = 0; i < G.vernum; i++) {
if (G.ver[i] == v1) {
return i;
}
}
return -1;
}
//创建无向图
bool createUDN(AMGraph &G) {
cout << "Please input the vernum and arcnum" << endl;
cin >> G.vernum >> G.arcnum;
cout << "Please input the name of points" << endl;
int i = 0,j=0;
for ( i = 0; i < G.vernum; i++)
cin >> G.ver[i];
//初始化邻接矩阵
for (i = 0; i < G.vernum; i++) {
for (j = 0; j < G.vernum; j++) {
if (i != j)
G.arc[i][j] = MaxInt;
else G.arc[i][j] = 0;
}
}
//开始操作,构造邻接表
cout << "Please input arcnum graph " << endl;
VerType v1, v2;
ArcType w;
int p, q;
for (i = 0; i < G.arcnum; i++) {
cin >> v1 >> v2 >> w;
p = Locate(G, v1); q = Locate(G, v2);
G.arc[p][q] = w;
G.arc[q][p] = G.arc[p][q];
}
return true;
}
void show(AMGraph G) {
int i, j;
for (i = 0; i < G.vernum; i++) {
for (j = 0; j < G.vernum; j++) {
if (G.arc[i][j] == MaxInt) printf("INF\t");
else printf("%-3d\t", G.arc[i][j]);
}
cout << endl;
}
}
// Prim算法部分
//辅组数组,记录U到V-U具有最小权值的边,入U的标准是lowCost=0(U是最小路径解集)
struct closedge {
VerType point;
ArcType lowCost;//worth of road
}closeEdge[ MaxNum ];
//思想:U是最小路径解集
//1.首先,把初始顶点u加入U中,对其余的每一个顶点vj,将closeEdge[j]均初始化为到u的边的信息。
//2.循环(n-1)次,循环内容:
// 从剩下没选过的各组边closeEdge[]中选择最小边closeEdge[K],输出此边
// 将k加入U中
// 更新剩余的每组边的最小边信息close[j],对于V-U中的边,新增加了一个j-k的边,如果这条边小于closeEdge[j],则更新为前者的值
int Min(closedge a[],int n) {
int mark = 0, min = MaxInt;
for (int i = 0; i < n; i++) {
if (a[i].lowCost!=0 && min > a[i].lowCost) {
mark = i;
min = a[i].lowCost;
}
}
return mark;
}
void MiniSpanTree_Prim(AMGraph G, VerType u) {
int k = Locate(G, u); //k为u下标
int j = 0;
//初始化closeEdge数组
for ( j = 0; j < G.vernum; j++)
if (j != k)
closeEdge[j] = { u,G.arc[k][j] };
closeEdge[k].lowCost = 0;//初始化到现在,U={u},入U的标准是lowCost=0
VerType u0=0; //最小边的顶点
VerType v0=0;//最小边的另一个顶点
for (int i = 1; i < G.vernum; i++) {//选择剩下的(n-1)个顶点,生成(n-1)条边
k = Min(closeEdge, G.vernum);
u0 = closeEdge[k].point;//最小边的顶点,属于U
v0 = G.ver[k]; //最小边的另一个顶点,属于V-U
//把v0融于U
closeEdge[k].lowCost = 0;
cout <<"the small arc begin at point " <<u0 << " ,and end at point " << v0 << endl;
//更新剩余的每组边的最小边信息close[j],对于V-U中的边,新增加了一个j-k的边,如果这条边小于closeEdge[j],则更新为前者的值
for (j = 0; j < G.vernum; j++) { //剩下的点到k值与原来的值比较
if (G.arc[k][j] < closeEdge[j].lowCost) {
closeEdge[j].lowCost = G.arc[k][j];//边值交换,新点进入U后,为了方便重新选择最小边
closeEdge[j].point = G.ver[k];//存放的是点的名字
}
}
}
}
int main() {
AMGraph G;
createUDN(G);
// show(G);
cout << "Please input the initial point:" << endl;
VerType u; cin >> u;
MiniSpanTree_Prim(G, u);
}
/*
测试案例
7 9
0 1 2 3 4 5 6
0 1 28
0 5 10
1 2 16
1 6 14
5 4 25
4 6 24
4 3 22
2 3 12
3 6 18
*/
/*
7 9
a b c d e f g
a b 28
a f 10
b c 16
b g 14
f e 25
e g 24
e d 22
c d 12
d g 18
*/
prim算法时间复杂度尾O(n^2),与网的边数没有关系,适合求稠密网的最小生成树。