跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像AI 生图工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

Flood Fill 洪水填充算法:经典题型实战与总结

Flood Fill 洪水填充算法利用 DFS 或 BFS 遍历连通块,广泛应用于图像处理与网格统计。核心场景包括图像渲染、岛屿计数与面积计算、被围绕区域反转、多洋流路径分析及扫雷模拟。关键技巧在于正难则反,例如从边界反向标记安全区域,避免重复访问需配合标记数组。C++ 实现需注意边界条件与递归终止逻辑,防止死循环。

BigDataPan发布于 2026/3/24更新于 2026/7/2135 浏览
Flood Fill 洪水填充算法:经典题型实战与总结

Flood Fill 算法概述

Flood Fill(洪水填充)是图论中处理连通块问题的基础算法。核心思想是通过 DFS 或 BFS 遍历具有相同性质的相邻节点,将其归为一类并执行相应操作。在网格问题中,这通常意味着从起点出发,向上下左右四个方向扩展,直到遇到边界或不同性质的节点。

图像渲染

这是最典型的 Flood Fill 应用。给定一个二维矩阵代表图像,以及起始坐标和新的颜色值,要求将起始点所在的连通区域全部染成新颜色。

图像渲染示意图

关键点:

  1. 记录原始颜色值,只有当邻居颜色等于原始颜色时才继续递归。
  2. 死循环陷阱: 如果起始点的颜色已经等于目标颜色,直接返回原矩阵。否则程序会陷入无限递归,因为当前点会被标记为目标色,但逻辑上它仍满足'颜色相同'的条件,导致反复访问。
class Solution {
public:
    int m, n;
    int originalColor; // 记录需要修改的原始颜色

    vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int color) {
        originalColor = image[sr][sc];
        m = image.size();
        n = image[0].size();

        // 避免死循环:如果起始点已经是目标颜色,无需修改
        if (originalColor == color) return image;

        dfs(image, sr, sc, color);
        return image;
    }

    int dx[4] = {-1, 1, 0, 0};
    int dy[4] = {0, 0, -1, 1};

    void dfs(vector<vector<int>>& image, int i, int j, int color) {
        image[i][j] = color;
        for (int k = 0; k < 4; k++) {
            int x = dx[k] + i;
            int y = dy[k] + j;
            // 检查边界且颜色匹配
            if (x >= 0 && x < m && y >= 0 && y < n && image[x][y] == originalColor) {
                dfs(image, x, y, color);
            }
        }
    }
};

岛屿数量

给定由 '1'(陆地)和 '0'(水)组成的二维网格,计算岛屿的数量。岛屿被水包围,且通过水平或垂直方向相连。

岛屿数量示意图

思路: 遍历整个网格,一旦发现 '1',说明发现了一个新岛屿,计数器加一。然后立即启动 DFS,将该岛屿所有相连的 '1' 都标记为 '0'(海水),这样后续遍历时就不会重复统计。

class Solution {
public:
    int m, n;
    int count = 0;

    int numIslands(vector<vector<char>>& grid) {
        if (grid.empty()) return 0;
        m = grid.size();
        n = grid[0].size();

        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (grid[i][j] == '1') {
                    dfs(grid, i, j);
                    count++;
                }
            }
        }
        return count;
    }

    int dx[4] = {0, 0, -1, 1};
    int dy[4] = {-1, 1, 0, 0};

    void dfs(vector<vector<char>>& grid, int i, int j) {
        // 将访问过的陆地标记为水,防止重复访问
        grid[i][j] = '0';
        for (int k = 0; k < 4; k++) {
            int x = dx[k] + i;
            int y = dy[k] + j;
            if (x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == '1') {
                dfs(grid, x, y);
            }
        }
    }
};

岛屿的最大面积

在岛屿数量的基础上,我们需要计算每个岛屿包含的格子数,并找出最大值。

岛屿最大面积示意图

思路: 使用 visited 数组标记已访问的格子。每次遇到未访问的陆地,重置当前面积计数器,DFS 过程中每访问一个有效格子就累加面积。遍历结束后更新全局最大值。

class Solution {
public:
    int m, n;
    int maxArea = 0;
    int currentArea = 0;
    bool vis[51][51];

    int maxAreaOfIsland(vector<vector<int>>& grid) {
        if (grid.empty()) return 0;
        m = grid.size();
        n = grid[0].size();
        memset(vis, 0, sizeof(vis));

        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (!vis[i][j] && grid[i][j] == 1) {
                    currentArea = 0;
                    dfs(grid, i, j);
                    maxArea = max(maxArea, currentArea);
                }
            }
        }
        return maxArea;
    }

    int dx[4] = {-1, 1, 0, 0};
    int dy[4] = {0, 0, -1, 1};

    void dfs(vector<vector<int>>& grid, int i, int j) {
        vis[i][j] = true;
        currentArea++;
        for (int k = 0; k < 4; k++) {
            int x = dx[k] + i;
            int y = dy[k] + j;
            if (x >= 0 && x < m && y >= 0 && y < n && !vis[x][y] && grid[x][y] == 1) {
                dfs(grid, x, y);
            }
        }
    }
};

被围绕的区域

给定一个二维矩阵,其中包含 'O' 和 'X'。要求将所有被 'X' 围绕的 'O' 替换为 'X'。

被围绕的区域示意图

思路: 正难则反。直接找被围绕的 'O' 很难判断是否连通到边界。不如先找到所有与边界相连的 'O',将它们标记为特殊字符(如 '.')。最后,矩阵中剩余的 'O' 必然是被围绕的,将其改为 'X';而标记为 '.' 的还原为 'O'。

class Solution {
public:
    int m, n;
    void solve(vector<vector<char>>& board) {
        if (board.empty()) return;
        m = board.size();
        n = board[0].size();

        // 1. 从四条边界的 'O' 开始 DFS,标记所有连通的 'O'
        for (int j = 0; j < n; j++) {
            if (board[0][j] == 'O') dfs(board, 0, j);
            if (board[m - 1][j] == 'O') dfs(board, m - 1, j);
        }
        for (int i = 0; i < m; i++) {
            if (board[i][0] == 'O') dfs(board, i, 0);
            if (board[i][n - 1] == 'O') dfs(board, i, n - 1);
        }

        // 2. 遍历矩阵,'.' 还原为 'O',剩余的 'O' 改为 'X'
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (board[i][j] == '.') board[i][j] = 'O';
                else if (board[i][j] == 'O') board[i][j] = 'X';
            }
        }
    }

    int dx[4] = {-1, 1, 0, 0};
    int dy[4] = {0, 0, -1, 1};

    void dfs(vector<vector<char>>& board, int i, int j) {
        board[i][j] = '.';
        for (int k = 0; k < 4; k++) {
            int x = dx[k] + i;
            int y = dy[k] + j;
            if (x >= 0 && x < m && y >= 0 && y < n && board[x][y] == 'O') {
                dfs(board, x, y);
            }
        }
    }
};

太平洋大西洋水流问题

给定一个非负整数矩阵表示高度,水可以从高处流向低处或相等高度。求哪些格子既能流向太平洋,又能流向大西洋。

水流问题示意图

思路: 正向搜索容易超时且逻辑复杂。采用反向思维:从海洋边缘向内陆搜索。分别维护两个布尔矩阵 pacific 和 atlantic,记录从对应海洋能到达的格子。最后取交集即可。

class Solution {
public:
    int m, n;
    vector<vector<int>> pacificAtlantic(vector<vector<int>>& heights) {
        vector<vector<int>> ret;
        if (heights.empty()) return ret;
        m = heights.size();
        n = heights[0].size();

        vector<vector<bool>> pac(m, vector<bool>(n, false));
        vector<vector<bool>> atl(m, vector<bool>(n, false));

        // 从太平洋边界(左、上)和大西洋边界(右、下)开始 DFS
        for (int j = 0; j < n; j++) {
            dfs(heights, 0, j, pac);
            dfs(heights, m - 1, j, atl);
        }
        for (int i = 0; i < m; i++) {
            dfs(heights, i, 0, pac);
            dfs(heights, i, n - 1, atl);
        }

        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (pac[i][j] && atl[i][j]) {
                    ret.push_back({i, j});
                }
            }
        }
        return ret;
    }

    int dx[4] = {-1, 1, 0, 0};
    int dy[4] = {0, 0, -1, 1};

    void dfs(vector<vector<int>>& heights, int i, int j, vector<vector<bool>>& ocean) {
        ocean[i][j] = true;
        for (int k = 0; k < 4; k++) {
            int x = dx[k] + i;
            int y = dy[k] + j;
            // 反向搜索:只能从低处流向高处(即当前点 <= 邻居点)
            if (x >= 0 && x < m && y >= 0 && y < n && 
                heights[i][j] <= heights[x][y] && !ocean[x][y]) {
                dfs(heights, x, y, ocean);
            }
        }
    }
};

扫雷游戏

模拟扫雷游戏的点击逻辑。如果点到地雷显示 'X',如果点到空地显示周围地雷数,如果是空白格则自动展开。

扫雷游戏示意图

思路:

  1. 检测点击位置是否为地雷 'M',是则直接变 'X' 结束。
  2. 否则统计周围 8 个方向的地雷数。如果有地雷,显示数字并停止。
  3. 如果没有地雷,显示 'B',并递归展开周围 8 个方向的空白格 'E'。
class Solution {
public:
    int m, n;
    int dx[8] = {-1, 1, 0, 0, 1, 1, -1, -1};
    int dy[8] = {0, 0, -1, 1, 1, -1, 1, -1};

    vector<vector<char>> updateBoard(vector<vector<char>>& board, vector<int>& click) {
        m = board.size();
        n = board[0].size();
        int x = click[0], y = click[1];

        if (board[x][y] == 'M') {
            board[x][y] = 'X';
            return board;
        }

        dfs(board, x, y);
        return board;
    }

    void dfs(vector<vector<char>>& board, int i, int j) {
        int count = 0;
        // 统计周围地雷数
        for (int k = 0; k < 8; k++) {
            int x = dx[k] + i;
            int y = dy[k] + j;
            if (x >= 0 && x < m && y >= 0 && y < n && board[x][y] == 'M') {
                count++;
            }
        }

        if (count > 0) {
            board[i][j] = count + '0';
        } else {
            board[i][j] = 'B';
            for (int k = 0; k < 8; k++) {
                int x = dx[k] + i;
                int y = dy[k] + j;
                if (x >= 0 && x < m && y >= 0 && y < n && board[x][y] == 'E') {
                    dfs(board, x, y);
                }
            }
        }
    }
};

衣橱整理

机器人从 (0,0) 出发,只能向右或向下移动。限制条件是坐标数位之和不能超过 k。求能到达多少个格子。

衣橱整理示意图

思路: 这是一个典型的 DFS 计数问题。注意数位和的计算方式,以及只能向右或向下移动的限制(相比其他题目减少了搜索方向)。使用 visited 数组避免重复访问。

class Solution {
public:
    int m, n;
    int count = 0;
    bool vis[101][101];

    int wardrobeFinishing(int _m, int _n, int cnt) {
        m = _m;
        n = _n;
        dfs(0, 0, cnt);
        return count;
    }

    int dx[2] = {1, 0};
    int dy[2] = {0, 1};

    void dfs(int i, int j, int cnt) {
        for (int k = 0; k < 2; k++) {
            int x = dx[k] + i;
            int y = dy[k] + j;
            if (x >= 0 && x < m && y >= 0 && y < n && !vis[x][y]) {
                if (digitSum(x) + digitSum(y) <= cnt) {
                    count++;
                    vis[x][y] = true;
                    dfs(x, y, cnt);
                }
            }
        }
    }

    int digitSum(int n) {
        int sum = 0;
        while (n) {
            sum += n % 10;
            n /= 10;
        }
        return sum;
    }
};

总结

通过这一系列题目,我们可以总结出 Flood Fill 算法的几个核心要点:

  1. 正难则反: 在处理复杂约束时(如被围绕区域、水流问题),尝试反向思考往往能简化逻辑。例如从边界向内标记,而不是从内部向外寻找。
  2. 连通块本质: 无论是图像渲染还是岛屿统计,本质上都是在寻找性质相同的连通分量。DFS 是最自然的实现方式,BFS 同样适用。
  3. 细节决定成败: 注意边界条件检查、避免死循环(如起始点颜色不变的情况)、合理使用标记数组防止重复访问。
  4. 方向控制: 根据题目要求灵活调整搜索方向(4 方向、8 方向或特定方向),代码结构可复用,只需修改方向数组即可。

目录

  1. Flood Fill 算法概述
  2. 图像渲染
  3. 岛屿数量
  4. 岛屿的最大面积
  5. 被围绕的区域
  6. 太平洋大西洋水流问题
  7. 扫雷游戏
  8. 衣橱整理
  9. 总结
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • C++ 多容器非空检查的逻辑陷阱与最佳实践
  • 医疗 AI 驱动下医院数据仓库的智能化升级:异构采集与精准评估
  • 全球老龄化社会护理机器人发展研究
  • Python 基于 Web 的师资管理系统设计与实现
  • Visual C++ 运行库检测工具原型开发 (Python+PyQt)
  • AI 零基础入门指南:从概念到实践
  • 贪心算法实战:柠檬水找零、数组减半与最大数拼接
  • C 语言初阶数据结构习题(二)
  • OpenClaw 生态 16 款 AI Agent 选型指南
  • Windows 7 系统 Python 3.8+ 兼容安装指南
  • Flutter 鸿蒙适配:基于 eip55 的以太坊地址校验方案
  • 智能家居界面美化指南:Home Assistant 主题配置与布局优化
  • 学生成绩综合统计分析系统的设计与实现
  • webdav-server 轻量级部署与实战配置指南
  • Java 继承与多态详解
  • 使用 Nginx 部署前端 Vue 项目指南
  • C++ 异常机制详解与实践指南
  • Flow取代LiveData的必要性分析
  • OpenClaw + 本地 Ollama:个人 AI 助手实战指南
  • Spring Boot 与 Vue 实现 WebSocket 实时匹配系统

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online