十二、图的算法入门--(1)连通分量和FloodFill算法

摘自计蒜客:http://www.jisuanke.com/course/35/7547

一、连通分量和FloodFill算法

首先,我们来介绍一个概念:子图(subgraph)。若一个图的顶点集和边集分别是另一图的顶点集的子集和边集的子集,则称该图为另一图的子图。
换句话说,从一个图里选出一部分顶点和边,只要确保选择的边对应的两个顶点也都被选择,那么所有选出的顶点和边组成的图就是原图的子图。
就像在一个社交网络中,同班同学的帐号之间的关系就组成了整个社交网络的一个子图。


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

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

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

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

1. 找到一个没有染色的顶点,将其染为新的颜色 Color_new,如果没有则算法结束。
2. 初始化一个空的队列,并将第一步的顶点插入队列。
3. 不断获得队首元素的值并弹出,将和队首元素相邻的未染色顶点染为Color_new,并将其加入队列。

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

下图是FloodFill算法的演示:


可以看出,最终整张图被染成了三种颜色,也就是说图中有三个连通分量。




FloodFill 的时间复杂度是 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>[n];
        color = new int[n];
        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() {
        //首先定义变量color_cnt表示当前染色的值
        int color_cnt = 0;
        //接下来,枚举所有未染色的顶点。顶点的染色结果记录在
        //color数组中,未染色时结果为0,否则为一个正数。因为我们判断一个顶点是否
        //染色时,只需要判断color数组中对应的值是否为0即可。
        for (int i=0; i<n; i++) {
            if (color[i] == 0) {
                //接下来,要从当前顶点出发,对所在连通分量进行广度优先遍历。
                //每次对所在连通分量的遍历,都会讲连通分量内的所有顶点染为一种
                //新的颜色。我们只需要将这些颜色区分开就可以,这里我们不妨让每
                //个连通分量染色的值和上次相比都加1.比如没一个连通分量染色的值
                //为1.第二个染色的值为2,依次类推。
                //在处理每一个连通分量时,第一步要做的就是将color_cnt的值加1,
                //并将第i个顶点染为color_cnt这个颜色。
                color_cnt++;
                color[i] = color_cnt;
                //进行广度优先遍历
                queue<int> q;
                q.push(i);
                while(!q.empty()) {
                    int vertex = q.front();
                    //枚举和vertex相邻的所有顶点
                    for(int j: edges[vertex]) {
                        if(color[j] == 0) {
                            color[j] = color_cnt;
                            q.push(j);
                        }
                    }
                    q.pop();
                }       
            }
        }
        //将color数组结果输出
        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;
}




  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
首先,你需要在 Visual Studio 中安装 FreeGLUT 库。可以在官网下载对应版本的库,并按照官方指南进行安装。 接下来,你可以创建一个空的 C++ 项目,并在项目属性中添加 FreeGLUT 库的链接。具体步骤如下: 1. 右键点击项目,选择“属性”。 2. 在左侧面板中选择“VC++ 目录”。 3. 在“包含目录”中添加 FreeGLUT 的 include 文件夹路径。 4. 在“库目录”中添加 FreeGLUT 的 lib 文件夹路径。 5. 在“链接器 -> 输入 -> 附加依赖项”中添加 FreeGLUT 的 lib 文件名(例如:freeglut.lib)。 完成上述步骤后,你可以开始编写四连通边界填算法和泛滥填充算法的代码。以下是一个简单的示例: ```cpp #include <GL/freeglut.h> void drawPixel(int x, int y) { glBegin(GL_POINTS); glVertex2i(x, y); glEnd(); } void boundaryFill4(int x, int y, float* fillColor, float* borderColor) { float interiorColor[3]; glReadPixels(x, y, 1, 1, GL_RGB, GL_FLOAT, interiorColor); if (interiorColor[0] != borderColor[0] || interiorColor[1] != borderColor[1] || interiorColor[2] != borderColor[2]) { drawPixel(x, y); glFlush(); boundaryFill4(x + 1, y, fillColor, borderColor); boundaryFill4(x - 1, y, fillColor, borderColor); boundaryFill4(x, y + 1, fillColor, borderColor); boundaryFill4(x, y - 1, fillColor, borderColor); } } void floodFill(int x, int y, float* fillColor, float* borderColor) { float interiorColor[3]; glReadPixels(x, y, 1, 1, GL_RGB, GL_FLOAT, interiorColor); if (interiorColor[0] != borderColor[0] || interiorColor[1] != borderColor[1] || interiorColor[2] != borderColor[2]) { drawPixel(x, y); glFlush(); floodFill(x + 1, y, fillColor, borderColor); floodFill(x - 1, y, fillColor, borderColor); floodFill(x, y + 1, fillColor, borderColor); floodFill(x, y - 1, fillColor, borderColor); } } void display() { float fillColor[] = { 1.0, 1.0, 0.0 }; float borderColor[] = { 0.0, 0.0, 0.0 }; glClearColor(1.0, 1.0, 1.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); glColor3f(0.0, 0.0, 0.0); glBegin(GL_LINE_LOOP); glVertex2i(100, 100); glVertex2i(200, 100); glVertex2i(200, 200); glVertex2i(100, 200); glEnd(); boundaryFill4(150, 150, fillColor, borderColor); //floodFill(150, 150, fillColor, borderColor); glFlush(); } int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); glutInitWindowSize(400, 400); glutCreateWindow("Boundary Fill and Flood Fill Algorithms"); gluOrtho2D(0, 400, 0, 400); glutDisplayFunc(display); glutMainLoop(); return 0; } ``` 该示例中包含了两个函数:`boundaryFill4` 和 `floodFill`,它们分别实现了四连通边界填算法和泛滥填充算法。在 `display` 函数中,我们先画了一个矩形,并在其中心点处调用了这两个函数。 注意,该示例中只实现了单色填充。如果你需要用不同的颜色填充不同区域,可以在 `drawPixel` 函数中添加更多参数,并在 `boundaryFill4` 和 `floodFill` 函数中进行判断和处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值