最小生成树之PRIM算法C++代码实现:
#include <iostream>
#include <vector>
using namespace std;
void prim(vector<vector<int>>& VGraph, vector<int>& lowcost, vector<int>& closest, vector<bool>& visited)
{
int size = lowcost.size(); //M
visited[0] = true; //从顶点0出发构造最小生成树
for (int i = 1; i < size; i++)
{
lowcost[i] = VGraph[0][i]; //初始化为顶点0到其他顶点i的权
closest[i] = 0; //i节点依附的节点
visited[i] = false;
}
int weight = 0; //最小生成树权值
cout << "输出生成树上各条边:" << endl;
//m-1条边,循环m-1次,每次找到一条边
for (int i = 0; i < size-1; i++)
{
int min = 99999;
int index = 1;
//寻找具有最小代价的边
for (int j = 0; j < size; j++)
{
//从刚并入U的顶点到其他点j (j必须未被访问过)的权中选出具有最小代价的边
if (lowcost[j] < min && !visited[j])
{
min = lowcost[j];
index = j;
}
}
weight += min;
//打印每次选择的边
cout << "(" << closest[index] << "," << index << ")" << endl;
visited[index] = true;
//因为新加入了index点,所以要查找新加入的index点到未在S中的点K中的权值是不是可以因此更小
for (int j = 1; j < size; j++)
{
if ((VGraph[index][j] < lowcost[j]) && (!visited[j])) //lowcost表示从已知树到某一点所需付出的最小权值
{
lowcost[j] = VGraph[index][j]; //更新刚加入的index点到其他j点的权值
closest[j] = index; //将j节点依附的节点修改为index
}
}
}
cout << "\n最小生成树权值为:" << weight << endl;
}
int main()
{
int M, N; //M为顶点数 N为边数
cin >> M >> N;
vector<vector<int>> VGraph(M,vector<int>(M)); //图的邻接矩阵
vector<int> lowcost(M); //刚选中并入U的顶点到其他顶点的权,初始为顶点0到其他点的权,因为从0出发
vector<int> closest(M); //记录每个节点依附的具有最小代价的节点,由此可得到最小生成树上的所有边
vector<bool> visited(M); //标记节点是否已访问
//数组表示 邻接矩阵初始化
for (int i = 0; i < M; i++)
{
for (int j = 0; j < M; j++)
{
VGraph[i][j] = 99999;
}
}
for (int i = 0; i < N; i++)
{
int a, b; //两个顶点
cin >> a >> b;
int length; //权
cin >> length;
VGraph[a][b] = VGraph[b][a] = length; //初始化图各条边
}
prim(VGraph, lowcost, closest, visited);
}
//0 1 6 0 2 1 0 3 5 1 2 5 1 4 3 2 3 5 2 4 6 2 5 4 3 5 2 4 5 6
复杂度分析:
假设网中有m个顶点,则第一个初始话的循环语句频度为m; 第二个找m-1条边的循环语句频度为m-1, 其中有两个内循环:其一是求具有最小代价的边,其频度为m, 其二是重新选择具有最小代价的边,其频度为m。 因此,PRIM算法总的时间复杂度为O(m^2), 与网中的边数无关,因此适用于求边稠密的网的最小生成树。
Kruskal算法
并查集实现
//并查集
vector<int> father(100);
vector<int> Rank(100);
//初始化
void init(int n){ //n个节点
for(int i=0;i<n;++i){
father[i] = i;
Rank[i] = 1;
}
}
//查询节点的根节点(路径压缩)
// int find(int x){
// if(x==father[x]){
// return x;
// }
// father[x] = find(father[x]); //查询过程中把沿途的每个节点的父节点都设为根节点
// return father[x];
// }
//非递归
int find(int x){
int root = father[x];
while(root != father[root]){ //找根节点
root = father[root];
}
//将路径上的所有节点的父节点设为根节点
while(x != root){
int t = father[x];
father[x] = root;
x = t;
}
return root;
}
//合并
void unit(int x,int y){
int fx = find(x);
int fy = find(y); //先找到两个根节点
if(Rank[fx]<=Rank[fy]){
father[fx] = fy;
}
else{
father[fy] = fx;
}
if(Rank[fx]==Rank[fy] && fx != fy){
Rank[fy]++;
}
}
struct edge{
int start;
int end;
int weight;
edge(){}
edge(int s,int e,int w):start(s),end(e),weight(w){}
};
bool cmp(edge& a,edge& b){
return a.weight<b.weight;
}
int main(){
int m,n; //顶点,边
cin>>m>>n;
// vector<vector<int>> G(m,vector<int> (m,INT_MAX));
vector<edge> G;
//图初始化
int a,b,w;
for(int i=0;i<n;++i){
cin>>a>>b>>w;
// G[a][b] = G[b][a] = length;
edge e(a,b,w);
G.push_back(e);
}
//kruskal
vector<int> res; //存最小生成树的每条边的索引
int res_weight = 0;
sort(G.begin(),G.end(),cmp); //边排序
init(m); //并查集初始化
int nEdge = 0; //最小生成树中的边数:m-1条
for(int i = 0; i < n && nEdge != m-1; ++i){
if(find(G[i].start) != find(G[i].end)){
unit(G[i].start,G[i].end);
res.push_back(i);
res_weight += G[i].weight;
nEdge++;
}
}
//如果加入边的数量小于m - 1,则表明该无向图不连通,等价于不存在最小生成树
if(nEdge < m-1){
cout<<"error!"<<endl;
return 0;
}
for(int i=0;i<m-1;++i){
cout<<G[res[i]].start<<" "<<G[res[i]].end<<ends;
}
cout<<endl;
cout<<res_weight<<endl;
return 0;
}