转载自:https://blog.csdn.net/qq_35644234/article/details/59106779#commentsedit
记录两个最小生成树算法,一个是Prim算法,一个是kruskal算法, 前者针对点展开,适合稠密图,后者针对边来展开,所以适合稀疏图(这里的稠密稀疏是对边来说的);
1.prim算法,时间复杂度为O(N^2),N是顶点的个数
prim算法的基本思想是:从图中的一个起点a开始,顶点a加入集合U中,剩下的顶点在集合(V-U)中,然后寻找与a关联的边的权值最小的一个顶点b,也将其加入到集合U中,此时集合U里就有a,b两个顶点;然后在与a关联和与b关联的边中,权重最小的那条边为c且其在集合(V-U)中,则把顶点c加入集合U中,此时U集合中有三个顶点a,b,c;以此类推,知道将所有顶点都加入了集合U;
#include<stdio.h>
#include<iostream>
#include<vector>
#include<algorithm>
#include<limits.h>
using namespace std;
//prim算法求最小生成树(使用邻接矩阵)
struct Graph
{
int vertexs; //顶点个数
int edges; //边的条数
int **mat; //邻接矩阵
};
void creatGraph(Graph &g)
{
cin >> g.vertexs;//输入顶点个数
cin >> g.edges; //输入边的条数
g.mat = new int*[g.vertexs];
int i = 0;
for (int i = 0; i < g.vertexs; i++)//初始化邻接矩阵
{
g.mat[i] = new int[g.vertexs];
for (int k = 0; k < g.vertexs; k++)
g.mat[i][k] = INT_MAX;
}
for (i = 0; i < g.edges; i++)//根据输入的边的权值更新邻接矩阵
{
int start;
int end;
int weight;
cin >> start >> end >> weight;
g.mat[start][end] = weight;
g.mat[end][start] = weight; //因为是无向边;
}
}
struct Assis_array
{
int start; //边的起点
int end; //边的终点
int weight;//边的权重
};
void prim(Graph g, int begin)
{
//close_edge这个数组记录到达某个顶点的各个边中权重最小的那个边
Assis_array *close_edge = new Assis_array[g.vertexs];
int j;
//对close_edge进行初始化
for (j = 0; j < g.vertexs; j++)
{
if (j != (begin))
{
close_edge[j].start = begin;
close_edge[j].end = j;
close_edge[j].weight = g.mat[begin][j];
}
}
close_edge[begin ].weight = -1;//把起点加入到集合U中
//依次访问剩下的顶点,将其加入集合U中
for (j = 1; j < g.vertexs; j++)
{
int min = INT_MAX;
int k, index;
//寻找数组close_edge中权重最小的那个边;
for (int k = 0; k < g.vertexs; k++)
{
if (close_edge[k].weight != -1)//该节点属于V-U集合
{
if (close_edge[k].weight < min)
{
min = close_edge[k].weight;
index = k;
}
}
}
close_edge[index].weight = -1;//将权重最小的边的终点加入到集合U中;
//更新close_edge数组;
for (k = 0; k < g.vertexs; k++)
{
if (g.mat[close_edge[index].end][k] < close_edge[k].weight)
{
close_edge[k].weight = g.mat[close_edge[index].end][k];
close_edge[k].start = close_edge[index].end;
close_edge[k].end = k;
}
}
}
}
int main()
{
Graph g;
creatGraph(g); //构造图的邻接矩阵
prim(g,0); //执行prim算法
return 0;
}
2.Kruskal算法
是一种贪心算法,将所有边按照权值从小到大排序,每次将最小的边加入到图中,添加的过程中不能形成回路,直至边可以连接所有顶点,时间复杂度为O(eloge),e表示边的数量;
#include<stdio.h>
#include<iostream>
#include<vector>
#include<algorithm>
#include<limits.h>
using namespace std;
//kruskal算法求最小生成树
//检查输入的顶点数和边数是否有效
bool check(int vertexs, int edges)
{
if (vertexs <= 0 || edges == 0 || (vertexs*(vertexs - 1) / 2) < edges)
return false;
return true;
}
//检查输入的边的信息是否是合法的
bool check_edge(int vertexs, int start, int end, int weight)
{
if (start<0 || end<0 || start>vertexs || end>vertexs || weight < 0)
return false;
return true;
}
//边集结构,用于保存每条边的信息
typedef struct edge_tag {
bool visited; //表示该条边是否已经加入到最小生成树中
int start; //边的起点
int end; //边的终点
int weight; //边的权值
}Edge;
//根据输入的边的信息构造图;
void creatGraph(Edge * &e,int vertexs,int edges)
{
e = new Edge[edges];
int start = 0;
int end = 0;
int weight = 0;
int i = 0;
while (i != edges)
{
cin >> start >> end >> weight;
while (!check_edge(vertexs, start, end, weight))
cin >> start >> end >> weight;
e[i].start = start;
e[i].end = end;
e[i].weight = weight;
i++;
}
}
//对边集进行排序,根据边的权值从小到大排序
int comp(const void* first, const void* second)
{
return ((Edge*)first)->weight - ((Edge*)second)->weight;
}
//parent表示顶点所在子树的根节点;
//child表示每颗子树的孩子节点的个数;
int find_root(int child, int *parent)
{
//表示已经找到了该顶点所在树的根节点;
if (parent[child] == child)
return child;
//往前递归,寻找其父节点所在树的根节点
parent[child] = find_root(parent[child],parent);
return parent[child];
}
bool union_tree(Edge e, int *parent, int *child)
{
int root1;//表示该边起点所在子树的根节点;
int root2;//表示该边终点所在子树的根节点;
root1 = find_root(e.start, parent);
root2 = find_root(e.end, parent);
//只有两个顶点不在同一个子树上,才可以把两棵树并为一棵树
if (root1 != root2)
{
//根据孩子的个数来合并,小树并到大树上
if (child[root1] > child[root2])
{
parent[root2] = root1;//把r2并到r1上
child[root1] += child[root2] + 1;//更新r1的孩子数,+1是表示r2的根节点
}
else
{
parent[root1] = root2;//把r1并到r2上;
child[root2] += child[root1] + 1;
}
return true;
}
return false;
}
void kruskal()
{
int vertexs = 0;
int edges = 0;
cin >> vertexs >> edges;
while (!check(vertexs, edges))
cin >> vertexs >> edges;
Edge *edge_tag;
creatGraph(edge_tag,vertexs,edges);
int *parent = new int[vertexs];
int *child = new int[vertexs];
int i=0,count_vex=0;//count_vex表示生成树中边的数量;
for (int i = 0; i < vertexs; i++)
{
parent[i] = i;//每个顶点的父节点是其自身
child[i] = 0;
}
//对边集进行排序
qsort(edge_tag,edges,sizeof(Edge),comp);
while (i != edges)
{
//如果两棵树可以组合在一起,说明该边是生成树的一条边
if (union_tree(edge_tag[i], parent, child))
{
edge_tag[i].visited = true;
count_vex++;
}
if (count_vex == vertexs - 1)
break;
i++;
}
if (count_vex != vertexs - 1)
cout << "无法构成连通图" << endl;
}
int main()
{
kruskal(); //执行kruskal算法
return 0;
}