前言
树:同时满足极小连通图与极大无环图
支撑树:能够覆盖图中每一个顶点的树
最小支撑树:有权网络中满足各边权值之和最小的支撑树
对于一幅图来说,存在的最小支撑树并不唯一。
一、Prim算法(由小树长成大树)
(1)思路
最小支撑树总是会采用联结每一割的最短跨越边。
用我的理解,就是不断拓展树的大小,用贪心思想,在目前的最小支撑树的邻接点中搜索与树相连的边的最小权值,并将其加入树,之后更新树的邻接点的数据,直到树能够覆盖所有的顶点
适用于稠密图,O(|V|^2)
(2)代码实现
#include<bits/stdc++.h>
#define MAX 0x3f3f3f;
using namespace std;
int sum;
int n,m;//输入的N个点,和M条边
int x,y,z;//输入变量
int i,j,k;//循环变量
int book[100];//用于记录这个点有没有被访问过
int dis[100];//用于记录距离树的距离最短路程
int maps[100][100];//用于记录所有边的关系
int Prim(){
int min,minIndex;
//初始化距离数组,默认先把离1点最近的找出来放好
for (i = 1; i <= n; i++)
dis[i] = maps[1][i];
book[1]=1;//记录1已经被访问过了
for (i = 1; i <= n-1; i++){//1已经访问过了,所以循环n-1次
min = MAX;//对于最小值赋值
for (j = 1; j <= n; j++){//搜索树的邻接点与树相连的边的权最小值及该顶点
if(!book[j]&& dis[j] < min)
{
min = dis[j];
minIndex = j;
}
}
//记录这个点已经被访问过了
book[minIndex] = 1;
sum += dis[minIndex];
for (j = 1; j <= n; j++){
//刚刚树增加了一个点,现在把与这个点相连的边的数据更新为更小的权值
//通过这一步骤,我们可以在dis里面存入树的邻接点与树相连的边的权值
if(book[j] == 0 && maps[minIndex][j] < dis[j])
dis[j] = maps[minIndex][j];
}
}
}
int main(){
cin>>n>>m;
//----------------------------------------------------
//初始化maps,除了自己到自己是0其他都是边界值
for (i = 1; i <= n; i++){
for (j = 1; j <= n; j++){
if(i!=j){
maps[i][j] = MAX;
}
else maps[i][j] = 0;
}
}
for (i = 1; i <= m; i++){
cin>>x>>y>>z;//输入的为无向图
maps[x][y] = z;
maps[y][x] = z;
}
//----------------------------------------------------
cout<<Prim()<<endl;
}
二、kruskal算法(合木成林)
(1)思路
贪心思想,每次会在剩余的边中选出权值最小的边,从而构成一棵棵子树,最终合木成林,但对于边的选择却存在要求,边不能属于同一棵子树,否则就形成了环路,不符合树的要求,直至边数等于总顶点数减一,结束检索。
适用于稀疏图,O(|V|*log|V|)
(2)代码实现
#include <bits/stdc++.h>
using namespace std;
struct Edge{
int u;
int v;
int w;
};
int parent[201];
//----------------------------------------
bool cmp(Edge e1, Edge e2){//对于结构体内部数据排序所需要
return e1.w < e2.w;
}
//----------------------------------------
int find(int x) {//递归求子树最终根节点
if (x == parent[x])
return x;
return parent[x] = find(parent[x]);//更新每个点的最终根节点
}
//----------------------------------------
int merge(int x, int y){//判断是否属于同一棵子树
int a = find(x);
int b = find(y);
if (a != b) {
parent[b] = a;
return 1;
}
return 0;
}
int main(){
int n, e;
while (cin >> n >> e){
Edge edge[e];
for (int i = 0; i <e; i++){//进行初始化
parent[i] = i;//先保证不同
}
for (int i = 0; i < e; i++){
cin>>edge[i].u>>edge[i].v>>edge[i].w;
}
sort(edge,edge+e,cmp);
int cnt = 0, sum = 0;//cnt为树中的边数,sum为树中各边之和
for (int i = 0; i < e; i++){
if (merge(edge[i].u, edge[i].v)){
cnt++;
sum += edge[i].w;
}
if (cnt == n - 1)
break;
}
if (cnt == n - 1)
cout << sum << endl;
else
cout <<"NO TREE"<< endl;
}
return 0;
}