连通图:在无向图中,若任意两个顶点vivi与vjvj都有路径相通,则称该无向图为连通图。
强连通图:在有向图中,若任意两个顶点vivi与vjvj都有路径相通,则称该有向图为强连通图。
连通网:在连通图中,若图的边具有一定的意义,每一条边都对应着一个数,称为权;权代表着连接连个顶点的代价,称这种连通图叫做连通网。
生成树:一个连通图的生成树是指一个连通子图,它含有图中全部n个顶点,但只有足以构成一棵树的n-1条边。一颗有n个顶点的生成树有且仅有n-1条边,如果生成树中再添加一条边,则必定成环。
最小生成树:在连通网的所有生成树中,所有边的代价和最小的生成树,称为最小生成树。
Kruskal算法
此算法可以称为“加边法”,初始最小生成树边数为0,每迭代一次就选择一条满足条件的最小代价边,加入到最小生成树的边集合里。
- 把图中的所有边按代价从小到大排序;
- 把图中的n个顶点看成独立的n棵树组成的森林;
- 按权值从小到大选择边,所选的边连接的两个顶点ui,vi应属于两颗不同的树,则成为最小生成树的一条边,并将这两颗树合并作为一颗树。
- 重复(3),直到所有顶点都在一颗树内或者有n-1条边为止。
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 1e5+5;
int n, m, a, b, v;
struct Edge
{
int fr, to, l;
};
bool cmp(Edge e1, Edge e2)
{
return e1.l < e2.l;
}
Edge graph[N];
Edge tree[N];
int fa[N];
int find(int x)
{
return fa[x] == x ? x : fa[x] = find(fa[x]);
}
void merge(int x, int y)
{
fa[find(x)] = find(y);
}
void Kruskal()
{
for(int i = 1; i <= n; ++i)
fa[i] = i;
sort(graph, graph+m, cmp);
for(int i = 0, j = 0; i < n-1 && j < m; ++j)
{
Edge curr = graph[j];
if(find(curr.fr) != find(curr.to))
{
tree[i++] = curr;
merge(curr.fr, curr.to);
}
}
}
int main()
{
cin >> n >> m;
for(int i = 0; i < m; ++i)
cin >> graph[i].fr >> graph[i].to >> graph[i].l;
Kruskal();
for(int i = 0; i < n-1; ++i)
{
Edge e = tree[i];
cout << e.fr << "->" << e.to << ":" << e.l << endl;
}
return 0;
}
Prim算法
输入:
6 10
1 2 6
1 3 1
1 4 5
2 3 5
2 5 3
3 5 6
3 6 4
4 3 5
4 6 2
5 6 6
#include<iostream>
#include<string>
#include<vector>
#include<new>
using namespace std;
typedef long long ll;
struct Graph{
int vexnum; //顶点个数
int edge; //边的条数
int ** arc; //邻接矩阵
string *information; //记录每个顶点名称
};
//创建图
void createGraph(Graph &g){
cin >> g.vexnum >> 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; //无向图的边是相反的
}
}
//打印图
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);
return 0;
}
转载请注明出处:勿在浮沙筑高台http://blog.youkuaiyun.com/luoshixian099/article/details/51908175