您的位置:首页 > 其它

连通图_连通分量FloodFill

2016-06-01 19:12 281 查看
首先,我们来介绍一个概念:子图(subgraph)。若一个图的顶点集和边集分别是另一图的顶点集的子集和边集的子集,则称该图为另一图的子图。

换句话说,从一个图里选出一部分顶点和边,只要确保选择的边对应的两个顶点也都被选择,那么所有选出的顶点和边组成的图就是原图的子图。

就像在一个社交网络中,同班同学的帐号之间的关系就组成了整个社交网络的一个子图。

接下来,我们介绍一个概念:连通。在无向图中,如果有从顶点 vv 到顶点 ww 的路径存在,则称 vv 和 ww 是连通的。若图 GG 中任意两个顶点都是连通的,则称图 GG 为连通图,否则成为非连通图。

若图 G 的子图Gs是连通的,我们就称子图 G​s是图 G 的连通子图。如果对于图 G 的一个连通子图G​s,不存在图 G 的其他连通子图 G​max满足:Gs是 Gmax的子图。则子图 G​s 是图 G的极大连通子图,也就是图 G 的连通分量。

如何求解无向图的连通分量呢?这要用到我们本章介绍的第一个图论算法:FloodFill 算法。

FloodFill 算法通常译作“洪水灌溉法”,算法通过给图中的顶点染色,最终使得同一个连通分量的顶点颜色相同,不同连通分量的顶点颜色不同。算法的描述如下:

找到一个没有染色的顶点,将其染为新的颜色 Color_{new}Color

​new

​​ ,如果没有则算法结束。

初始化一个空的队列,并将第一步的顶点插入队列。

不断获得队首元素的值并弹出,将和队首元素相邻的未染色顶点染为 Color_{new}Color

​new

​​ ,并将其加入队列。

重复执行第一步,直到所有顶点都被染色,算法结束。

FloodFill 的时间复杂度是 O(V+E)O(V+E),其中广度优先遍历的部分可以替换成深度优先遍历,复杂度是一样的。通常考虑到递归调用的时间开销,往往广度优先遍历的效率要更高一些。

#include <iostream>
#include <vector>
#include <cstring>
#include <queue>

using namespace std;

class Graph {
private:
int n;
int * color;
vector<int> * edges;

public:
Graph(int input_n) {
n = input_n;
edges = new vector<int>
;
color = new int
;
memset(color, 0, n * sizeof(int));
}

~Graph() {
delete[] edges;
delete[] color;
}

void insert(int x, int y) {
edges[x].push_back(y);
edges[y].push_back(x);
}

void floodfill() {
int color_cnt=0;
for(int i=0;i<n;++i){
if(color[i]==0){
//不同联通分量内的结点标记不同颜色
color_cnt++;
color[i]=color_cnt;
queue<int> q;
//未标记结点入队
q.push(i);
while(!q.empty()){
int vertex= q.front();
//依次标记与之有边的结点,并将标记后的节点入队
for(int j:edges[vertex]){
if(color[j]==0){
color[j]=color_cnt;
q.push(j);
}
}
q.pop();
}
}
}
for(int i=0;i<n;++i){
cout<<i<<" "<<color[i]<<endl;
}
}
};

int main() {
int n, m, k;
cin >> n >> m;
Graph g(n);
for (int i = 0; i < m; ++i) {
int x, y;
cin >> x >> y;
g.insert(x, y);
}
g.floodfill();

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  森林