最小生成树
概念:在无向图中求一棵树T,使得这个树拥有图G中所有顶点,且所有边都是来自图G中的边,并且满足整颗树的边权之和最小,这棵树就是图G的最小生成树。
特征:
- 最小生成树是树,因此边的数量等于顶点数减1,并且树内一定不会有环
- 图G生成的最小生成树,最小生成树可能不唯一,但是边权之和必然唯一
- 由于是无向图,所以根据给出的结点开始生成最小生成树即可。
Kruskal算法
步骤
- 以边的角度出发,将所有边按权值大小排序,先将边从小到大加入到集合中,
- 看添加边后有没有形成环,若形成环,则不要这条边。判断是否形成环用到了并查集结构,是通过判断当前添加的边的两端结点是否在同一连通域内得出是否形成环,若在同一集合,说明添加这条边以后就能形成环。
- 没有形成环时,将这条边加入到结果中,同时将边的两端点合并为一个集合。
set<Edge*> kruskalMST(Graph graph) {
UnionFind unionfind(graph);
priority_queue<Edge*, vector<Edge*>, cmp>priQueue;
for (Edge* edge : graph.edges) {
priQueue.push(edge);
}
set<Edge*>result;
while (!priQueue.empty()) {
Edge* edg = priQueue.top();
priQueue.pop();
if (!unionfind.isSameSet(edg->from, edg->to)) {
result.insert(edg);
unionfind.un(edg->from, edg->to);
}
}
return result;
}
prim算法
思想:
- 设置一个集合st存放已经访问过的点,选择一个点加入到st中,把该点的所有边都加入到小根堆中。
- 从堆中弹出一个权值最小的边,获取到边的另一端的结点,判断这个结点是否在集合中,如果不在就将这个边加入结果,这个点也加入集合。同时再将这个点的邻边全部加入小根堆。
- 不断从堆中弹出最小边,就可以找到最小生成树,如果这个图是一棵树,经过这个循环就可以找到最终结果,break即可。但是如果这个图是森林,即有多个连通域,就需要把所有点都过一遍(最外层的循环)。
set<Edge*> primMST(Graph graph) {
priority_queue<Edge*, vector<Edge*>, cmp>priQueue;
set<Node*>st;
set<Edge*>result;
for (auto it = graph.nodes.begin(); it != graph.nodes.end(); it++) {
//选择一个点加入到st中
if (st.find(it->second) == st.end()) {
st.insert(it->second);
//将该点的所有边都加入到小根堆中
for (auto j : it->second->edges) {
priQueue.push(j);
}
//只要堆不为空,就从堆中弹出最小的一条边
while (!priQueue.empty()) {
Edge* edge = priQueue.top();
priQueue.pop();
//获取当前边的下一个点
Node* node = edge->to;
//如果下一个点已经在st集合中,说明形成环,就不要这个点,继续弹出最小边
if (st.find(node) == st.end()) {
//如果新的点不在st集合中,说明当前点可行,那么就将这个点加入st集合,这条边加入result集合
st.insert(node);
result.insert(edge);
//再将新加入点的邻边都加入小根堆中
for (Edge* nextEdge : node->edges) {
priQueue.push(nextEdge);
}
}
}
}
//break;如果图是一整个连通域,就加上break,经过一系列操作就已经把树生成好了,如果是森林,就是多棵树,就需要将结点都遍历一次
}
}