图论算法——Kruskal算法
Kruskal 算法是针对最小生成树的一个算法,不优化的时间复杂度是O(e^2),用并查集优化的时间复杂度就是O(eloge)其中e为边数。
所以就可以看出,并查集对Kruskal的优化还是很大的。
我们先来看看Kruskal针对的到底是一个什么样的问题:
•输入一个有权无向连通图G,在已有的边中删掉尽可能多的边得到一个新的图G’。使得G’依旧是连通图并且剩余的边权值之和最小,请输出G’中所有边权之和。
所以说,最小生成树虽然是“树”,但这却是一个有关图论的问题。
Q:树怎么能跟图扯到一起呢?
A:树其实就是一个特殊的图,也就是树是一个没有环的图
Q:但问题中并没有提到“没有环”啊?
A:这就是问题的关键所在。
我们先来回答几个问题:
Q:问题中的Q’里不能有什么?
A:不能有环!
Q:为什么不能有环?
A:因为在一个在Q’中所有的环里的很多边都是多余的,为什么这么说么?看看下面这张图就知道了!
你看出什么了吗?这就是之前那个问题的答案!在Q’中是肯定不能有环的!有环也可以拆掉,只要保持联通就可以了。没有环的图就是什么?树!
解决了这些疑惑,就可以开始Kruskal算法了,Kruskal算法的步骤是:
1、 将G中所有点之间的边断开得到G’,然后将所有边按照权值从小到大排序不妨记为边集E。
2、 集合E中当前权值最小的边记为e,将e从E中删除。
3、 如果e连接的顶点v和u在G’中已经连通,转2。
这一点也就是并查集所能优化的一点,并查集可以判断这两个节点是否已经联通,如果你不知道并查集怎么用,有请戳并查集帮助1(戳我)或并查集帮助2(戳我)。
4、 将e,加入G’
5、 如果G’中边的数量等于n-1,算法结束,否则转2。
详见代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<vector>
class edge
{
public:
int u;
int v;
int weight;
};
bool operator< (const edge& a,const edge& b)
{
return a.weight<b.weight;
}
std::vector<edge> edges;
int father[1001];
int get_father(int x)
{
if(father[x]==x)return x;
return father[x] = get_father(father[x]);
}
int main()
{
int n,m;
std::cin>>n>>m;
edges.resize(m+1);
for(int i = 0;i<m;i++)
std::cin>>edges[i].u>>edges[i].v>>edges[i].weight;
std::sort(edges.begin(),edges.end());
for(int i = 0;i<n;i++)father[i] = i;
int ans = 0;
for(int i = 0;i<m;i++)
{
if(get_father(edges[i].u)==get_father(edges[i].v))continue;
ans += edges[i].weight;
father[get_father(edges[i].u)] = father[get_father(edges[i].v)];
}
std::cout<<ans;
return 0;
}
如果代码有问题,欢迎留言!
这里有几个初学者不会很知道的东西,给出几个帮助:
vector:vector帮助1(戳我),vector帮助2(戳我)。
sort:sort帮助1(戳我)
operator:帮助1