10.5.1 最小生成树及其性质

10.5.2 prim算法:(求一个无向图中的最小生成树)





prim算法的具体实现:



prim的邻接矩阵法+邻接表法:
/*
邻接矩阵版本:
1.设置最大顶点数,和INF数
2.设置顶点数n和二维数组G
3.设置bool类型 数组 vis 标记其值均为false
int类型的prim函数
1.默认从0号为初始起点,函数返回最小生成树的边权之和
2.通过fill,将数组d初始化为INF
3.设置0号顶点的距离为0
4.设置int类型 ans为0
5.和Dijie一样双层for循环找出距离最小的顶点和距离MIN
6.如果u==-1,返回-1
7.标价u已经被访问过
8.将d[u]的值加入到ans中
9.for循环v从0开始,一直到n,如果vis中没有访问过,u能到v,u为中介点,是v到集合的距离更小,则更新距离
10.最后放回ans
*/
const int maxn=1000;
const int INF=10000000l;
int n,G[maxn][maxn],d[maxn];
bool vis[maxn]={false};
int prim()
{
fill(d,d+maxn,INF);
d[0]=0;
int ans=0;
for(int i=0; i<n; i++)
{
int u=-1,MIN=INF;
for(int j=0; j<n; j++)
if(vis[j]==false && d[j]<MIN)
{
u=j;
MIN=d[j];
}
if(u==-1)
return -1;
vis[u]=true;
ans+=d[u];
for(int v=0; v<n; v++)
{
if(vis[v]==false && G[u][v]!=INF && G[u][v]<d[v])
d[v]=G[u][v];
}
}
return ans;
}
/*
邻接表版本:
1.设置结构类型node:两个结构成员:v,dis
2.设置vector的结构二维数组
3.设置顶点数n,顶点距离集合最短的距离d,bool类型 vis
int类型prim函数:
1.初始化d的值为INF
2.在d中设置0号位置的距离为0
3.设置最小边权值为0
4.双层for不变
5.如果u为-1,则返回-1,标记u被访问过,将距离累加到ans中
6.for循环,j从0开始,遍历结点u下的长度
1.标记v是当前的顶点v
2.更新距离
7.最后返回ans值
*/
const int maxn=1000;
const int INF=10000000l;
struct node
{
int v;
int dis;
};
vector<node> Adj[maxn];
int n,d[maxn];
bool vis[maxn]={false};
int prim()
{
fill(d,d+maxn,INF);
d[0]=0;
int ans=0;
for(int i=0; i<n; i++)
{
int u=-1,MIN=INF;
for(int j=0; j<n; j++)
if(vis[j]==false && d[j]<MIN)
{
u=j;
MIN=d[j];
}
if(u==-1)
return -1;
vis[u]=true;
ans+=d[u];
for(int j=0; j<Adj[u].size(); j++)
{
int v=Adj[u][j].v;
if(vis[v]==false && d[v]>Adj[u][j].dis)
d[v]=Adj[u][j].dis;
}
}
return ans;
}
亚历山大攻打恶魔的例子:
//邻接矩阵法:
#include<iostream>
using namespace std;
const int maxn=100;
const int INF=1000000;
int G[maxn][maxn],n,m,d[maxn];
bool vis[maxn]={false};
int prime()
{
fill(d,d+maxn,INF);
d[0]=0;
int ans=0;
for(int i=0; i<n; i++)
{
int u=-1,MIN=INF;
for(int j=0; j<n; j++)
{
if(vis[j]==false && d[j]<MIN)
{
u=j;
MIN=d[j];
}
}
if(u==-1)
return -1;
vis[u]=true;
ans+=d[u];
for(int v=0; v<n; v++)
{
if(vis[v]==false && G[u][v]!=INF && d[v]>G[u][v])
d[v]=G[u][v];
}
}
return ans;
}
int main()
{
cin >> n >> m;
fill(G[0],G[0]+maxn*maxn,INF);
for(int i=0; i<m; i++)
{
int a,b,c;
cin >> a >> b >> c;
G[a][b]=c;
G[b][a]=c;
}
cout << prime();
return 0;
}
//邻接表法:
#include<iostream>
#include<vector>
using namespace std;
const int maxn=100;
const int INF=1000000;
int n,m,d[maxn];
bool vis[maxn]={false};
struct node
{
int v,dis;
node(int _v,int _dis):v(_v),dis(_dis){};
};
vector<node> Adj[maxn];
int prime()
{
fill(d,d+maxn,INF);
d[0]=0;
int ans=0;
for(int i=0; i<n; i++)
{
int u=-1,MIN=INF;
for(int j=0; j<n; j++)
{
if(vis[j]==false && d[j]<MIN)
{
u=j;
MIN=d[j];
}
}
if(u==-1)
return -1;
vis[u]=true;
ans+=d[u];
for(int j=0; j<Adj[u].size(); j++)
{
int v=Adj[u][j].v;
if(vis[v]==false && d[v]>Adj[u][j].dis)
d[v]=Adj[u][j].dis;
}
}
return ans;
}
int main()
{
cin >> n >> m;
for(int i=0; i<m; i++)
{
int a,b,c;
cin >> a >> b >> c;
Adj[a].push_back(node(b,c));
Adj[b].push_back(node(a,c));
}
cout << prime();
return 0;
}
10.5.3 kruskal算法:(求一个无向图中的最小生成树)


//krusla算法的结构:
struct node
{
int u,v; //边的两个端点编号
int cost; //边权
}E[maxn]; //最多有maxn条边
bool cmp(node a, node b)
{
return a.cost<b.cost;
}


/*
1.并查集数组:int类型 father[N]
2.int类型 并查集查询函数 findFather 形参为int类型 x
1.令a=x;
2.通过whil找到x的根结点
3.压缩路径
3.int类型 kruskal函数,形参为 int类型 n,m分别表示顶点数和图的边数
1.ans为所求边之和,Num_Edge为当前生成树的边数初始值均为0
2.for循环,对并查集数组初始化
3.调用sort函数,将所有边按照升序排列
4.for循环枚举所有的边
1.查询测试边两个端点的根结点
2.如果两个端点不在一个集合中,合并(将测试边加入到最小生成树中),累加边权
3.当前生成树的个数++
5.如果Num_Edge的值为n-1,结束算法
6.如果Num_Edge的值不为n-1,则返回-1
7.否则,返回最小生成树的边权之和
*/
int father[n];
int findFather(int x)
{
int a=x;
while(x!=father[x])
{
x=father[x];
}
while(a!=father[a])
{
int z=a;
a=father[a];
father[z]=x;
}
return x;
}
int kruskal(int n, int m)
{
int ans,Num_Edge;
ans=0;
Num_Edge=0;
for(int i=0; i<n; i++)
father[i]=i;
sort(E,E+m,cmp);
for(int i=0; i<m; i++)
{
int Fu=findFather(E[i].u);
int Fv=findFather(E[i].v);
if(Fu!=Fv)
{
E[Fu]=Fv;
ans+=E[i].cost;
Num_Edge++;
if(Num_Edge==n-1)
break;
}
}
if(Num_Edge!=n-1)
return -1;
else
return ans;
}
prim算法和krusal算法的不同使用情况
顶点数多+边数少=prim算法
顶点数少+边数多=krusal算法


#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=100;
const int INF=10000000;
struct node //设置一个结构
{
int u,v; //两个端点
int cost; //边权
}E[maxn];
int father[maxn],n,m;
bool cmp(node a, node b) //按照边权的升序排列
{
return a.cost<b.cost;
}
int findFather(int x) //找到x的根结点
{
int a=x;
while(x!=father[x])
x=father[x];
while(a!=father[a])
{
int z=a;
a=father[a];
father[z]=x;
}
return x;
}
int krusal(int n, int m)
{
int ans=0,Num_Edge=0; //设置ans为边权之和,Num_Edge为边数
for(int i=0; i<n; i++) //初始化并查集数组
father[i]=i;
sort(E,E+m,cmp); //将结构按照边权的升序排列
for(int i=0; i<m; i++) //遍历每条边
{
int Fu=findFather(E[i].u);
int Fv=findFather(E[i].v);
if(Fu!=Fv)
{
father[Fu]=Fv; //将Fu的根结点设为Fv,将两个集合合并
ans+=E[i].cost; //累加边权
Num_Edge++; //边权个数+1
if(Num_Edge==n-1) //如果边数为顶点数-1,则退出循环
break;
}
}
if(Num_Edge!=n-1)
return -1;
else
return ans; //返回最小生成树的权值
}
int main()
{
cin >> n >> m;
for(int i=0; i<m; i++)
{
int a,b,c;
cin >> a >> b >> c;
E[i].u=a;
E[i].v=b;
E[i].cost=c;
}
cout << krusal(n,m);
}
最短路径的方法:
本文详细介绍了Prim算法和Kruskal算法求解无向图最小生成树的过程,并提供了两种算法的具体实现代码,包括邻接矩阵法和邻接表法。同时对比了两种算法适用的不同场景。
5107

被折叠的 条评论
为什么被折叠?



