【递归、搜索与回溯算法】(floodfill算法:从不会做矩阵题,到真正掌握搜索扩散思想)


🔥承渊政道: 个人主页
❄️个人专栏: 《C语言基础语法知识》 《数据结构与算法》 《C++知识内容》 《Linux系统知识》 《算法刷题指南》 《测评文章活动推广》 《大模型语言路线学习》
✨逆境不吐心中苦,顺境不忘来时路!✨ 🎬 博主简介:

很多人在刚接触矩阵题时,都会有一种相似的感受:题目看得懂,例子也能明白,但一到真正动手写代码,就不知道该从哪里开始.尤其是遇到"连通块""岛屿数量""颜色填充""边界扩散"这类问题时,明明都是在一个二维网格里走来走去,却总觉得思路零散、方法混乱,写出来不是漏情况,就是陷入死循环.其实,这类题目背后往往藏着同一种核心思想------搜索与扩散.而在众多矩阵搜索模型中,Flood Fill(洪水填充)算法正是最经典、最基础、也最值得彻底掌握的一种.它不仅能帮助我们解决看似复杂的网格遍历问题,更重要的是,它能让我们真正建立起"从一个点出发,向四周递归扩展"的搜索直觉.很多人学 Flood Fill 时,只是记住了一个模板:找到起点,朝四个方向搜索,标记访问过的位置,然后继续递归.但如果只是停留在"会套代码"的层面,一旦题目稍微变形,比如加入边界限制、状态判断、多源扩散或者回溯选择,思路就又会变得模糊.真正的关键,从来不是背下模板,而是理解它背后的搜索逻辑:为什么能搜、往哪里搜、什么时候停、为什么不会重复搜.所以这篇文章,不只是想带你"学会一道 Flood Fill 模板题",而是想带你完成一次更重要的转变:从不会做矩阵题,到真正掌握搜索扩散思想.我们会从最基础的问题出发,拆清楚递归、搜索与回溯之间的关系,理解 Flood Fill 在矩阵中的运行机制,并通过典型题型总结出一套可以迁移、可以复用的思考框架.当你真正理解了 Flood Fill,你会发现,过去那些看起来杂乱无章的矩阵题,其实都在考同一件事:你能不能把"点"与"点之间的连接关系"想清楚,能不能把"局部扩散"转化成"整体搜索".而这,正是搜索算法最迷人的地方.废话不多说,下面跟着小编的节奏🎵一起去疯狂的学习吧!

目录

1.Flood Fill算法思想背景详细介绍

在正式学习 Flood Fill 之前,我们需要先理解:它为什么会出现,它解决的到底是哪一类问题,它和递归、搜索、连通性之间又是什么关系.

从表面上看,Flood Fill 常常出现在各种"矩阵题"里,例如颜色填充、岛屿数量、区域连通、封闭区域、边界扩散等问题.但从本质上说,它并不是只属于"矩阵技巧",而是一种非常典型的 图搜索思想在二维网格中的具体体现.
1.Flood Fill 是怎么来的

"Flood Fill"这个名字,直译过来就是"洪水填充"或"洪泛填充".

你可以想象这样一个场景:

有一块由很多小方格组成的区域,其中某些方格彼此连在一起.现在你从其中一个格子倒下一滴水,这滴水会不断向四周蔓延,只要相邻位置满足某种条件,它就会继续扩散.最终,所有和起点"连通"的、符合条件的格子,都会被这股"洪水"覆盖.这就是 Flood Fill 最直观的思想来源.

它最早常见于图形处理软件中,比如绘图工具中的"油漆桶填色"功能:当你点击某个区域时,程序会自动把和这个点相连、颜色相同的整片区域全部替换成新颜色.这个过程本质上就是一次 Flood Fill.所以,Flood Fill 并不是为了刷题而发明的,它本来就是一个非常自然的计算机问题:如何从一个起点出发,把所有满足连通条件的区域一次性找出来,并进行统一处理.

2.为什么矩阵题里总能看到 Flood Fill

很多初学者会觉得矩阵题种类特别杂:

  • 有的题让你数岛屿个数
  • 有的题让你染色
  • 有的题让你找最大连通块
  • 有的题让你判断是否能走到边界
  • 有的题让你把某些区域全部替换掉

看起来题意五花八门,但这些问题背后往往都隐藏着一个共同结构:

在一个二维网格中,从某个位置出发,沿着相邻关系不断扩展,寻找一个"连通区域".

而Flood Fill 正是为这种结构服务的.因为矩阵本身天然就是一个"离散的平面空间":

  • 每个格子可以看作一个节点
  • 相邻格子之间可以看作有边连接
  • 整个矩阵就构成了一张特殊的图

于是,很多矩阵题实际上都可以转化为这样一个问题:

在这张图里,从某个点开始搜索,与它连通的所有点有哪些?

一旦你这样看问题,就会发现,所谓矩阵题,本质上并不是"数组高级技巧",而是在二维场景下做 图的遍历与搜索.

3.Flood Fill 的本质:寻找"连通块"

Flood Fill 最核心的数学本质,可以概括为四个字:

寻找连通块.

所谓"连通块",就是在某种邻接规则下,彼此可以到达的一组点.

例如在矩阵中,如果我们规定:

  • 上下左右四个方向算相邻,那么这是"四连通"
  • 如果连对角线也算相邻,那么这是"八连通"

那么,只要一个格子可以通过若干步相邻移动走到另一个格子,它们就属于同一个连通块.

Flood Fill所做的事情,就是:

  1. 选择一个起点
  2. 看它周围哪些格子和它连通
  3. 再从这些格子继续扩展
  4. 最终把整个连通块全部找出来

这就是为什么 Flood Fill 特别适合解决下面这类问题:

  • 统计区域大小
  • 判断区域是否封闭
  • 修改整片区域状态
  • 标记已经访问过的连通部分
  • 遍历所有独立区域

所以从思想上说,Flood Fill 并不只是"填色",而是:

对某个连通区域进行整体发现、整体标记、整体处理的一种搜索策略.

4.Flood Fill 为什么经常和递归联系在一起

很多教材一讲 Flood Fill,第一反应就是递归写法.这是因为 Flood Fill 的扩散过程和递归的定义非常契合.递归最适合处理的问题,往往具有这样的特征:

一个大问题,可以拆成若干个结构相同、规模更小的子问题.

而 Flood Fill 正好满足这一点.

假设你现在站在某个格子 (x, y) 上,想要完成"从这里开始填充整块区域"的任务.那你会怎么做?

答案很自然:

  • 先处理当前格子
  • 然后尝试向上扩展
  • 尝试向下扩展
  • 尝试向左扩展
  • 尝试向右扩展

而"向某个方向扩展"之后,本质上又变成了同样的问题:

从新的格子出发,继续填充与它连通的区域.也就是说,原问题和子问题的结构是完全一致的.

这就使得递归成为 Flood Fill 最自然的表达方式.它把"整体扩散"拆成了无数个"从当前点继续向外扩散"的局部动作.所以,递归并不是 Flood Fill 的本质,递归只是表达 Flood Fill 的一种非常自然的实现形式.这一点非常重要.很多人误以为 Flood Fill 就是"背一个递归模板",但实际上你真正要理解的是:

  • 为什么能从当前点向四周扩展
  • 为什么每个方向都要继续搜索
  • 为什么已经访问过的点不能重复访问
  • 为什么最终能覆盖整个连通区域

这些才是算法思想本身.递归只是把这个过程写出来而已.

5.Flood Fill 和 DFS、BFS 的关系

从算法分类上说,Flood Fill 不是一个和 DFS、BFS 并列的新算法.

更准确地说:Flood Fill 是一种问题模型,而DFS/BFS是实现这种模型的搜索方式.

也就是说,Flood Fill 想解决的是:

  • 从一个起点出发
  • 把所有满足条件的连通位置都找出来

至于你是怎么找出来的,可以有两种典型方式:

  • 用DFS 实现 Flood Fill:DFS(深度优先搜索)会沿着一个方向不断深入,直到不能再走,再回头搜索其他方向.这很像水流先沿某条路径蔓延到底,再回过头覆盖其他区域.递归写法的Flood Fill,本质上通常就是DFS.
  • 用 BFS 实现 Flood Fill:BFS(广度优先搜索)则是一层一层向外扩展,先处理离起点近的,再处理远的.
    这更像水波纹从中心一圈一圈向外扩散.在某些"最短步数""多源扩散""层级传播"问题里,BFS 版本的 Flood Fill 更合适.

所以要明确:

  • Flood Fill 关注的是"连通区域扩散"
  • DFS/BFS关注的是"扩散的遍历顺序"

两者不是竞争关系,而是"模型"和"工具"的关系.

6.Flood Fill 背后的核心思想:扩散、覆盖、去重

如果再往深一点总结,Flood Fill 背后其实有三个关键词:

(1)扩散

从一个点出发,向邻居传播.这是搜索的起点,也是"局部影响整体"的体现.

(2)覆盖

不是只看一个点,而是要把整个连通区域都处理掉.这意味着算法关注的是"区域整体",不是单点答案.

(3)去重

任何已经访问过的位置都不能重复处理,否则会死循环或重复统计.所以 Flood Fill 一定和"标记访问"绑定在一起.


2.图像渲染(OJ题)


算法思路:

可以利用深搜或者宽搜,遍历到与该点相连的所有像素相同的点,然后将其修改成指定的像素即可.
递归函数设计:

  • 参数

    a. 原始矩阵;

    b. 当前所在的位置;

    c. 需要修改成的颜色.

  • 函数体

    a. 先将该位置的颜色改成指定颜色(因为我们的判断,保证每次进入递归的位置都是需要修改的位置);

    b. 遍历四个方向上的位置:如果当前位置合法,并且与初试颜色相同,就递归进去.

核心代码

cpp 复制代码
class Solution
{
//方向数组:定义上下左右四个移动方向(二维网格通用写法)
//dx: 行坐标偏移量  dy: 列坐标偏移量
int dx[4] = {0, 0, 1, -1};  //右、左、下、上
int dy[4] = {1, -1, 0, 0};
int m, n;                    //m=图像的行数  n=图像的列数
int prev;                    //记录起始点的原始颜色(需要被替换的颜色)

public:
    //主函数:图像渲染入口
    //image: 二维图像数组  sr:起始行  sc:起始列  color:目标填充颜色
    vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int color)
    {
        //边界条件:如果起始点已经是目标颜色,直接返回原图像(无需染色)
        if(image[sr][sc] == color) return image;
        
        //初始化图像尺寸
        m = image.size();          //获取总行数
        n = image[0].size();       //获取总列数
        prev = image[sr][sc];      //保存需要替换的原始颜色
        
        //调用DFS递归函数,开始填充颜色
        dfs(image, sr, sc, color);
        
        //返回修改后的图像
        return image;
    }

    //DFS深度优先搜索:递归染色函数
    //i,j: 当前正在染色的坐标  color:目标颜色
    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 = i + dx[k];
            int y = j + dy[k];
            
            //三个判断条件:
            //1. x在合法行范围内  2.y在合法列范围内  3.新坐标颜色=原始颜色(需要染色)
            if(x >= 0 && x < m && y >= 0 && y < n && image[x][y] == prev)
            {
                //递归:对符合条件的新坐标继续染色
                dfs(image, x, y, color);
            }
        }
    }
};

完整测试代码

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

class Solution
{
    int dx[4] = {0, 0, 1, -1};
    int dy[4] = {1, -1, 0, 0};
    int m, n;
    int prev;

public:
    vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int color)
    {
        if (image[sr][sc] == color) return image;
        m = image.size();
        n = image[0].size();
        prev = image[sr][sc];
        dfs(image, sr, sc, color);
        return image;
    }

    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 = i + dx[k], y = j + dy[k];
            if (x >= 0 && x < m && y >= 0 && y < n && image[x][y] == prev)
            {
                dfs(image, x, y, color);
            }
        }
    }
};

// 打印二维矩阵
void printImage(const vector<vector<int>>& image)
{
    for (const auto& row : image)
    {
        for (int val : row)
        {
            cout << val << " ";
        }
        cout << endl;
    }
}

int main()
{
    Solution sol;

    // 测试用例1
    vector<vector<int>> image1 = {
            {1, 1, 1},
            {1, 1, 0},
            {1, 0, 1}
    };
    int sr1 = 1, sc1 = 1, color1 = 2;

    cout << "原始图像1:" << endl;
    printImage(image1);

    vector<vector<int>> result1 = sol.floodFill(image1, sr1, sc1, color1);

    cout << "\n染色后的图像1:" << endl;
    printImage(result1);

    cout << "------------------------" << endl;

    // 测试用例2:起点颜色和目标颜色相同
    vector<vector<int>> image2 = {
            {0, 0, 0},
            {0, 1, 1}
    };
    int sr2 = 1, sc2 = 1, color2 = 1;

    cout << "原始图像2:" << endl;
    printImage(image2);

    vector<vector<int>> result2 = sol.floodFill(image2, sr2, sc2, color2);

    cout << "\n染色后的图像2:" << endl;
    printImage(result2);

    cout << "------------------------" << endl;

    // 测试用例3:单个点扩散
    vector<vector<int>> image3 = {
            {0, 0, 0},
            {0, 1, 1}
    };
    int sr3 = 1, sc3 = 1, color3 = 2;

    cout << "原始图像3:" << endl;
    printImage(image3);

    vector<vector<int>> result3 = sol.floodFill(image3, sr3, sc3, color3);

    cout << "\n染色后的图像3:" << endl;
    printImage(result3);

    return 0;
}

3.岛屿的数量(OJ题)


算法思路:解法(dfs):

遍历整个矩阵,每次找到一块陆地的时候:

  • 说明找到一个岛屿,记录到最终结果 ret 里面;
  • 并且将这个陆地相连的所有陆地,也就是这块岛屿,全部变成海洋.这样的话,我们下次遍历到这块岛屿的时候,它已经是海洋了,不会影响最终结果.
  • 其中变成海洋的操作,可以利用深搜和宽搜解决,其实就是图像渲染这道题

这样,当我们遍历完全部的矩阵的时候,ret 存的就是最终结果.
算法流程:

  1. 初始化 ret = 0,记录目前找到的岛屿数量;
  2. 双重循环遍历二维网格,每当遇到一块陆地,标记这是一个新的岛屿,然后将这块陆地相连的陆地全部变成海洋.

递归函数的设计:

  1. 把当前格子标记为水;
  2. 向上、下、左、右四格递归寻找陆地,只有在下标位置合理的情况下,才会进入递归:
    a. 下一个位置的坐标合理;
    b. 并且下一个位置是陆地.

核心代码

cpp 复制代码
class Solution
{
    //访问标记数组:标记网格中的位置是否被遍历过,避免重复统计
    vector<vector<bool>> vis;
    //m:网格的行数,n:网格的列数
    int m, n;

public:
    //主函数:统计岛屿数量
    //grid:二维字符网格,'1'代表陆地,'0'代表海水
    int numIslands(vector<vector<char>>& grid)
    {
        //获取网格的行数和列数
        m = grid.size(), n = grid[0].size();
        //初始化访问数组,大小和网格一致,默认值为false(未访问)
        vis = vector<vector<bool>>(m, vector<bool>(n));

        //记录岛屿的总数量
        int ret = 0;
        //双层循环:遍历网格中的每一个位置
        for(int i = 0; i < m; i++)
            for(int j = 0; j < n; j++)
            {
                //核心判断:当前位置未被访问 + 是陆地
                if(!vis[i][j] && grid[i][j] == '1')
                {
                    //发现新岛屿,数量+1
                    ret++;
                    //DFS遍历:标记这个岛屿所有连通的陆地为已访问
                    dfs(grid, i, j);
                }
            }
        //返回最终统计的岛屿数量
        return ret;
    }

    //方向数组:控制上下左右四个方向移动
    int dx[4] = {0, 0, -1, 1};  // 行偏移:右、左、上、下
    int dy[4] = {1, -1, 0, 0};  // 列偏移

    //DFS深度优先搜索:标记当前岛屿所有连通的陆地
    void dfs(vector<vector<char>>& grid, int i, int j)
    {
        //标记当前位置为已访问
        vis[i][j] = true;
        //遍历上下左右四个方向
        for(int k = 0; k < 4; k++)
        {
            //计算相邻位置的坐标
            int x = i + dx[k], y = j + dy[k];
            //判断条件:
            //1.坐标在网格范围内(不越界)
            //2.该位置未被访问
            //3.该位置是陆地
            if(x >= 0 && x < m && y >= 0 && y < n && !vis[x][y] && grid[x][y] == '1')
            {
                //递归遍历相邻的陆地
                dfs(grid, x, y);
            }
        }
    }
};

完整测试代码

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

class Solution
{
    vector<vector<bool>> vis;
    int m, n;

public:
    int numIslands(vector<vector<char>>& grid)
    {
        m = grid.size();
        n = grid[0].size();
        vis = vector<vector<bool>>(m, vector<bool>(n, false));

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

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

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

// 打印字符矩阵
void printGrid(const vector<vector<char>>& grid)
{
    for (const auto& row : grid)
    {
        for (char ch : row)
        {
            cout << ch << " ";
        }
        cout << endl;
    }
}

int main()
{
    Solution sol;

    // 测试用例1
    vector<vector<char>> grid1 = {
            {'1', '1', '1', '1', '0'},
            {'1', '1', '0', '1', '0'},
            {'1', '1', '0', '0', '0'},
            {'0', '0', '0', '0', '0'}
    };

    cout << "测试用例1:" << endl;
    printGrid(grid1);
    cout << "岛屿数量: " << sol.numIslands(grid1) << endl;
    cout << "----------------------" << endl;

    // 测试用例2
    vector<vector<char>> grid2 = {
            {'1', '1', '0', '0', '0'},
            {'1', '1', '0', '0', '0'},
            {'0', '0', '1', '0', '0'},
            {'0', '0', '0', '1', '1'}
    };

    cout << "测试用例2:" << endl;
    printGrid(grid2);
    cout << "岛屿数量: " << sol.numIslands(grid2) << endl;
    cout << "----------------------" << endl;

    // 测试用例3
    vector<vector<char>> grid3 = {
            {'1', '0', '1', '0', '1'},
            {'0', '1', '0', '1', '0'},
            {'1', '0', '1', '0', '1'}
    };

    cout << "测试用例3:" << endl;
    printGrid(grid3);
    cout << "岛屿数量: " << sol.numIslands(grid3) << endl;
    cout << "----------------------" << endl;

    return 0;
}

4.岛屿的最大面积(OJ题)


算法思路:解法(深搜 dfs):

  • 遍历整个矩阵,每当遇到一块土地的时候,就用深搜或者宽搜将与这块土地相连的整个岛屿的面积计算出来.
  • 然后在搜索得到的所有的岛屿面积求一个最大值即可.
  • 在搜索过程中,为了防止搜到重复的土地:
    • 可以开一个同等规模的布尔数组,标记一下这个位置是否已经被访问过;
    • 也可以将原始矩阵的 1 修改成 0,但是这样操作会修改原始矩阵.

算法流程:

  • 主函数内

    a. 遍历整个数组,发现一块没有遍历到的土地之后,就用 dfs,将与这块土地相连的岛屿的面积求出来;

    b. 然后将面积更新到最终结果 ret 中.

  • 深搜函数 dfs

    a. 能够进到 dfs 函数中,说明是一个没遍历到的位置;

    b. 标记一下已经遍历过,设置一个变量 S = 1(当前这个位置的面积为1),记录最终的面积;

    c. 上下左右遍历四个位置:如果找到一块没有遍历到的土地,就将与这块土地相连的岛屿面积累加到 S 上;

    d. 循环结束后,S 中存的就是整块岛屿的面积,返回即可.

核心代码

cpp 复制代码
class Solution
{
    //访问标记数组:固定大小51x51,标记网格位置是否被遍历过(题目限制网格最大50x50)
    bool vis[51][51];
    //m:网格行数,n:网格列数
    int m, n;
    //方向数组:上下左右四个方向偏移量(二维网格DFS通用写法)
    int dx[4] = {0, 0, 1, -1};
    int dy[4] = {1, -1, 0, 0};
    //统计单个岛屿的面积
    int count;

public:
    //主函数:求解最大岛屿面积
    //grid:二维网格,1=陆地,0=水域
    int maxAreaOfIsland(vector<vector<int>>& grid)
    {
        //获取网格的行数和列数
        m = grid.size(), n = grid[0].size();
        //记录最终的最大岛屿面积
        int ret = 0;

        //双层循环:遍历网格中每一个位置
        for(int i = 0; i < m; i++)
            for(int j = 0; j < n; j++)
                //核心条件:当前位置未被访问 + 是陆地
                if(!vis[i][j] && grid[i][j] == 1)
                {
                    count = 0;          //重置单个岛屿面积计数器
                    dfs(grid, i, j);    //DFS遍历整个岛屿,统计面积
                    ret = max(ret, count); //更新最大面积
                }
        //返回最大岛屿面积
        return ret;
    }

    //DFS深度优先搜索:递归遍历岛屿,统计陆地数量(面积)
    void dfs(vector<vector<int>>& grid, int i, int j)
    {
        count++;                //每访问一块陆地,面积+1
        vis[i][j] = true;       //标记当前陆地为已访问,避免重复统计

        //遍历上下左右四个方向
        for(int k = 0; k < 4; k++)
        {
            //计算相邻位置的坐标
            int x = i + dx[k], y = j + dy[k];
            //判断条件:
            //1.坐标不越界 2.未被访问 3.是陆地
            if(x >= 0 && x < m && y >= 0 && y < n && !vis[x][y] && grid[x][y] == 1)
            {
                dfs(grid, x, y); //递归遍历相邻陆地
            }
        }
    }
};

完整测试代码

cpp 复制代码
#include <iostream>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;

class Solution
{
    bool vis[51][51];
    int m, n;
    int dx[4] = {0, 0, 1, -1};
    int dy[4] = {1, -1, 0, 0};
    int count;

public:
    int maxAreaOfIsland(vector<vector<int>>& grid)
    {
        memset(vis, false, sizeof(vis)); // 初始化访问数组
        m = grid.size();
        n = grid[0].size();

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

    void dfs(vector<vector<int>>& grid, int i, int j)
    {
        count++;
        vis[i][j] = true;

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

// 打印网格
void printGrid(const vector<vector<int>>& grid)
{
    for (const auto& row : grid)
    {
        for (int val : row)
        {
            cout << val << " ";
        }
        cout << endl;
    }
}

int main()
{
    Solution sol;

    // 测试用例1
    vector<vector<int>> grid1 = {
            {0,0,1,0,0,0,1,1},
            {0,1,1,0,1,0,1,1},
            {0,0,0,0,1,0,0,0},
            {1,1,0,1,1,1,0,0},
            {1,1,0,0,0,0,0,1}
    };

    cout << "测试用例1:" << endl;
    printGrid(grid1);
    cout << "最大岛屿面积: " << sol.maxAreaOfIsland(grid1) << endl;
    cout << "------------------------" << endl;

    // 测试用例2
    vector<vector<int>> grid2 = {
            {1,1,0,0,0},
            {1,1,0,1,1},
            {0,0,0,1,1},
            {0,1,0,0,0}
    };

    cout << "测试用例2:" << endl;
    printGrid(grid2);
    cout << "最大岛屿面积: " << sol.maxAreaOfIsland(grid2) << endl;
    cout << "------------------------" << endl;

    // 测试用例3:全是海洋
    vector<vector<int>> grid3 = {
            {0,0,0},
            {0,0,0},
            {0,0,0}
    };

    cout << "测试用例3:" << endl;
    printGrid(grid3);
    cout << "最大岛屿面积: " << sol.maxAreaOfIsland(grid3) << endl;
    cout << "------------------------" << endl;

    // 测试用例4:全是陆地
    vector<vector<int>> grid4 = {
            {1,1,1},
            {1,1,1},
            {1,1,1}
    };

    cout << "测试用例4:" << endl;
    printGrid(grid4);
    cout << "最大岛屿面积: " << sol.maxAreaOfIsland(grid4) << endl;
    cout << "------------------------" << endl;

    return 0;
}

5.被围绕的区域(OJ题)


算法思路:

正难则反.可以先利⽤ dfs 将与边缘相连的 '0' 区域做上标记,然后重新遍历矩阵,将没有标记过的 '0'

修改成 'X' 即可.

核心代码

cpp 复制代码
class Solution
{
    //方向数组:定义上下左右四个移动方向(二维网格通用)
    int dx[4] = {1, -1, 0, 0};  //行偏移:下、上、左、右
    int dy[4] = {0, 0, 1, -1};  //列偏移
    // m:棋盘的行数,n:棋盘的列数
    int m, n;

public:
    //主函数:解决被围绕的区域(LeetCode 130)
    //规则:被 'X' 包围的 'O' 替换为 'X',边界上/连通边界的 'O' 保持不变
    void solve(vector<vector<char>>& board)
    {
        //边界防护:如果棋盘为空,直接返回,避免程序报错
        if (board.empty() || board[0].empty()) return;
        
        //获取棋盘的行数和列数
        m = board.size();
        n = board[0].size();

        //第一步:遍历【第一行和最后一行】的所有列
        //找到边界上的 '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);
        }

        //第二步:遍历【第一列和最后一列】的所有行
        //找到边界上的 'O',用DFS标记所有连通的 'O'
        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);
        }

        //第三步:遍历整个棋盘,完成最终替换
        for (int i = 0; i < m; i++)
        {
            for (int j = 0; j < n; j++)
            {
                if (board[i][j] == '.') 
                    board[i][j] = 'O';  //临时标记的 '.' 还原为 'O'(不会被包围)
                else if (board[i][j] == 'O') 
                    board[i][j] = 'X';  //剩余的 'O' 被包围,替换为 'X'
            }
        }
    }

    //DFS 深度优先搜索:将当前及连通的 'O' 标记为临时字符 '.'
    void dfs(vector<vector<char>>& board, int i, int j)
    {
        //标记当前位置为临时符号(区分不会被包围的O)
        board[i][j] = '.';
        
        //遍历上下左右四个方向
        for (int k = 0; k < 4; k++)
        {
            //计算相邻格子的坐标
            int x = i + dx[k], y = j + dy[k];
            //判断条件:坐标不越界 + 相邻格子是 'O'
            if (x >= 0 && x < m && y >= 0 && y < n && board[x][y] == 'O')
            {
                //递归标记连通的 'O'
                dfs(board, x, y);
            }
        }
    }
};

完整测试代码

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

class Solution
{
    int dx[4] = {1, -1, 0, 0};
    int dy[4] = {0, 0, 1, -1};
    int m, n;

public:
    void solve(vector<vector<char>>& board)
    {
        if (board.empty() || board[0].empty()) return;

        m = board.size();
        n = board[0].size();

        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);
        }

        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';
            }
        }
    }

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

void printBoard(const vector<vector<char>>& board)
{
    for (const auto& row : board)
    {
        for (char ch : row)
        {
            cout << ch << " ";
        }
        cout << endl;
    }
}

int main()
{
    Solution sol;

    vector<vector<char>> board = {
            {'X', 'X', 'X', 'X'},
            {'X', 'O', 'O', 'X'},
            {'X', 'X', 'O', 'X'},
            {'X', 'O', 'X', 'X'}
    };

    cout << "原始棋盘:" << endl;
    printBoard(board);

    sol.solve(board);

    cout << "\n处理后棋盘:" << endl;
    printBoard(board);

    return 0;
}

6.太平洋大西洋的水流问题(OJ题)


算法思路:

正难则反.如果直接去判断某⼀个位置是否既能到⼤西洋也能到太平洋,会重复遍历很多路径.我们反着来,从⼤西洋沿岸开始反向 dfs ,这样就能找出那些点可以流向⼤西洋;同理,从太平洋沿岸也反向 dfs ,这样就能找出那些点可以流向太平洋.那么,被标记两次的点,就是我们要找的结果.

核心代码

cpp 复制代码
class Solution
{
    //网格的行数、列数
    int m, n;
    //方向数组:上下左右四个方向(二维网格搜索通用)
    int dx[4] = {0, 0, 1, -1};
    int dy[4] = {1, -1, 0, 0};

public:
    //主函数:寻找既能流向太平洋也能流向大西洋的坐标
    //h: 高度矩阵,代表单元格的高度
    vector<vector<int>> pacificAtlantic(vector<vector<int>>& h)
    {
        m = h.size();
        n = h[0].size();
        
        //pac: 标记能否流向太平洋(Pacific)
        //atl: 标记能否流向大西洋(Atlantic)
        vector<vector<bool>> pac(m, vector<bool>(n));
        vector<vector<bool>> atl(m, vector<bool>(n));

        //1.处理【太平洋】(上边界和左边界)
        //从第一行所有列出发,能逆流到达的点都能流向太平洋
        for(int j = 0; j < n; j++) 
            dfs(h, 0, j, pac);
        //从第一列所有行出发,能逆流到达的点都能流向太平洋
        for(int i = 0; i < m; i++) 
            dfs(h, i, 0, pac);

        //2.处理【大西洋】(下边界和右边界)
        //从最后一列所有行出发
        for(int i = 0; i < m; i++) 
            dfs(h, i, n - 1, atl);
        //从最后一行所有列出发
        for(int j = 0; j < n; j++) 
            dfs(h, m - 1, j, atl);

        //3.收集结果
        //遍历所有坐标,如果该点既能流向太平洋又能流向大西洋,加入结果集
        vector<vector<int>> ret;
        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;
    }

    //DFS 深度优先搜索:逆流搜索
    //h: 高度矩阵
    //i,j: 当前搜索坐标
    //vis: 访问标记数组,标记哪些点可以逆流到达海洋
    void dfs(vector<vector<int>>& h, int i, int j, vector<vector<bool>>& vis)
    {
        //标记当前点已访问(表示可以从这里流向对应的海洋)
        vis[i][j] = true;

        //遍历上下左右四个方向
        for(int k = 0; k < 4; k++)
        {
            //计算相邻坐标
            int x = i + dx[k];
            int y = j + dy[k];
            
            //核心判断逻辑(逆流思想):
            //1. 坐标不越界
            //2. 该点未被访问
            //3. 相邻点的高度 >= 当前点的高度 (h[x][y] >= h[i][j])
            //解释:因为水是从高处流向低处,反之,逆流搜索时,只有下一个点比当前点高/等高,
            //才能说明水可以从下一个点流到当前点,进而流到海洋。
            if(x >= 0 && x < m && y >= 0 && y < n && !vis[x][y] && h[x][y] >= h[i][j])
            {
                dfs(h, x, y, vis);
            }
        }
    }
};

完整测试代码

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

class Solution
{
    int m, n;
    int dx[4] = {0, 0, 1, -1};
    int dy[4] = {1, -1, 0, 0};

public:
    vector<vector<int>> pacificAtlantic(vector<vector<int>>& h)
    {
        m = h.size();
        n = h[0].size();

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

        // 1. 从太平洋边界出发
        for (int j = 0; j < n; j++) dfs(h, 0, j, pac);
        for (int i = 0; i < m; i++) dfs(h, i, 0, pac);

        // 2. 从大西洋边界出发
        for (int i = 0; i < m; i++) dfs(h, i, n - 1, atl);
        for (int j = 0; j < n; j++) dfs(h, m - 1, j, atl);

        // 3. 同时能到达 pac 和 atl 的位置
        vector<vector<int>> ret;
        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;
    }

    void dfs(vector<vector<int>>& h, int i, int j, vector<vector<bool>>& vis)
    {
        if (vis[i][j]) return;   // 防止重复搜索
        vis[i][j] = true;

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

// 打印矩阵
void printMatrix(const vector<vector<int>>& h)
{
    for (const auto& row : h)
    {
        for (int x : row)
        {
            cout << x << " ";
        }
        cout << endl;
    }
}

// 打印结果坐标
void printResult(const vector<vector<int>>& ret)
{
    cout << "[";
    for (int i = 0; i < ret.size(); i++)
    {
        cout << "[" << ret[i][0] << "," << ret[i][1] << "]";
        if (i != ret.size() - 1) cout << ", ";
    }
    cout << "]" << endl;
}

int main()
{
    Solution sol;

    // 测试用例1
    vector<vector<int>> heights1 = {
            {1, 2, 2, 3, 5},
            {3, 2, 3, 4, 4},
            {2, 4, 5, 3, 1},
            {6, 7, 1, 4, 5},
            {5, 1, 1, 2, 4}
    };

    cout << "测试用例1:高度矩阵" << endl;
    printMatrix(heights1);
    vector<vector<int>> ans1 = sol.pacificAtlantic(heights1);
    cout << "结果坐标:" << endl;
    printResult(ans1);
    cout << "------------------------" << endl;

    // 测试用例2:单个格子
    vector<vector<int>> heights2 = {
            {1}
    };

    cout << "测试用例2:高度矩阵" << endl;
    printMatrix(heights2);
    vector<vector<int>> ans2 = sol.pacificAtlantic(heights2);
    cout << "结果坐标:" << endl;
    printResult(ans2);
    cout << "------------------------" << endl;

    // 测试用例3:所有高度相同
    vector<vector<int>> heights3 = {
            {1, 1},
            {1, 1}
    };

    cout << "测试用例3:高度矩阵" << endl;
    printMatrix(heights3);
    vector<vector<int>> ans3 = sol.pacificAtlantic(heights3);
    cout << "结果坐标:" << endl;
    printResult(ans3);
    cout << "------------------------" << endl;

    return 0;
}

7.扫雷游戏(OJ题)


算法思路:

模拟类型的 dfs 题⽬.⾸先要搞懂题⽬要求,也就是游戏规则.从题⽬所给的点击位置开始,根据游戏规则,来⼀次 dfs 即可.

核心代码

cpp 复制代码
class Solution
{
    //8个方向数组:上下左右 + 四个斜角(扫雷需要遍历8个相邻格子)
    int dx[8] = {0, 0, 1, -1, 1, 1, -1, -1};
    int dy[8] = {1, -1, 0, 0, 1, -1, 1, -1};
    //m:棋盘行数,n:棋盘列数
    int m, n;

public:
    //主函数:扫雷游戏点击更新逻辑
    //board:扫雷棋盘,click:用户点击的坐标 [行, 列]
    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]; //提取点击的坐标

        //情况1:点击的位置是未挖出的地雷 'M'
        if(board[x][y] == 'M') 
        {
            board[x][y] = 'X'; //将地雷标记为踩爆的状态 'X'
            return board;      //直接返回结果,游戏结束
        }

        //情况2:点击的是空方块,开始DFS递归翻开方块
        dfs(board, x, y);
        return board; //返回更新后的棋盘
    }

    //DFS深度优先搜索:递归翻开扫雷棋盘的方块
    void dfs(vector<vector<char>>& board, int i, int j)
    {
        //统计当前格子 8 个相邻方向的地雷总数
        int count = 0;
        for(int k = 0; k < 8; k++)
        {
            //计算相邻格子的坐标
            int x = i + dx[k], y = j + dy[k];
            //判断:坐标合法 + 相邻格子是未挖出的地雷 'M'
            if(x >= 0 && x < m && y >= 0 && y < n && board[x][y] == 'M')
            {
                count++; //地雷数量+1
            }
        }

        //分支1:当前格子周围有地雷,直接标记数字,停止递归
        if(count) 
        {
            board[i][j] = count + '0'; //数字转字符(0~8)
            return;
        }
        //分支2:当前格子周围无地雷,标记为空白方块 'B',继续递归翻开相邻方块
        else 
        {
            board[i][j] = 'B'; //标记为已翻开的空白方块
            //遍历8个方向,递归翻开所有未挖出的空方块 'E'
            for(int k = 0; k < 8; k++)
            {
                int x = i + dx[k], y = j + dy[k];
                //判断:坐标合法 + 相邻格子是未挖出的空方块 'E'
                if(x >= 0 && x < m && y >= 0 && y < n && board[x][y] == 'E')
                {
                    dfs(board, x, y); //递归翻开
                }
            }
        }
    }
};

完整测试代码

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

class Solution
{
    int dx[8] = {0, 0, 1, -1, 1, 1, -1, -1};
    int dy[8] = {1, -1, 0, 0, 1, -1, 1, -1};
    int m, n;

public:
    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)
    {
        if (board[i][j] != 'E') return;

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

        if (count > 0)
        {
            board[i][j] = count + '0';
            return;
        }

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

void printBoard(const vector<vector<char>>& board)
{
    for (const auto& row : board)
    {
        for (char ch : row)
        {
            cout << ch << " ";
        }
        cout << endl;
    }
}

int main()
{
    Solution sol;

    vector<vector<char>> board = {
            {'E', 'E', 'E', 'E', 'E'},
            {'E', 'E', 'M', 'E', 'E'},
            {'E', 'E', 'E', 'E', 'E'},
            {'E', 'E', 'E', 'E', 'E'}
    };
    vector<int> click = {3, 0};

    cout << "原始棋盘:" << endl;
    printBoard(board);

    vector<vector<char>> result = sol.updateBoard(board, click);

    cout << "\n更新后棋盘:" << endl;
    printBoard(result);

    return 0;
}

8.衣橱整理(OJ题)


算法思路:

这道题本质上是一道 矩阵搜索 / Flood Fill 问题.

我们把衣柜看成一个 m × n 的网格,从左上角 (0, 0) 出发.由于题目规定整理师每次只能 向右 或 向下 移动一格,因此我们可以用 深度优先搜索(DFS)去遍历所有能够到达的格子.

对于一个位置 (i, j),它能够被访问需要满足三个条件:

  1. 没有越界
  2. 之前没有访问过
  3. digit(i) + digit(j) <= cnt

只要满足条件,就说明这个格子是可以整理的,我们将答案加一,并继续向它的右边和下边扩展搜索.

为什么只搜右和下

虽然从图搜索角度看,一个格子通常有四个方向可以走,但这道题题目已经明确说明:

  • 只能向右移动一格
  • 或向下移动一格

所以我们没有必要再去搜索左和上.

只保留这两个方向:

  • (i, j + 1) 向右
  • (i + 1, j) 向下

这样既符合题意,也能减少不必要的搜索分支.

搜索过程

  1. 从起点 (0, 0) 开始
  2. 如果当前位置合法,就标记为已访问
  3. 将答案 ret 加一
  4. 继续递归搜索右边和下边两个方向
  5. 最终统计所有能够到达的格子数量

剪枝思想

为了避免重复访问同一个格子,需要使用一个 vis 数组进行标记.

当某个格子已经访问过之后,后续再次搜索到它时就直接跳过.

这样可以保证:

  • 每个格子最多只会被访问一次
  • 搜索不会死循环
  • 时间复杂度保持在线性范围内

数位和判断

题目要求只有当:

digit(i) + digit(j) <= cnt时,这个格子才需要整理.

所以我们单独写一个 check(i, j) 函数,用来计算坐标 i 和 j 的各位数字之和,并判断是否满足限制条件.

例如:

  • (2, 3) 的数位和是 2 + 3 = 5
  • (35, 7) 的数位和是 3 + 5 + 7 = 15

如果数位和大于 cnt,这个格子就不能进入.

核心代码

cpp 复制代码
class Solution
{
    int m, n, cnt;       // m:行数 n:列数 cnt:坐标数位之和的最大限制值
    bool vis[101][101];  // 访问标记数组,标记坐标是否被遍历过
    int ret;            // 统计最终能到达的合法格子数量
    // 方向数组:仅允许 向右(0,1)、向下(1,0) 两个方向移动
    int dx[2] = {0, 1};
    int dy[2] = {1, 0};

public:
    // 主函数:计算机器人能到达的衣柜格子总数
    int wardrobeFinishing(int _m, int _n, int _cnt)
    {
        // 初始化参数
        m = _m;
        n = _n;
        cnt = _cnt;
        ret = 0;
        // 初始化访问数组为 false(未访问)
        memset(vis, false, sizeof(vis));

        // 边界判断:起点(0,0) 本身就不满足数位和条件,直接返回0
        if (!check(0, 0)) return 0;

        // 从起点 (0,0) 开始DFS搜索
        dfs(0, 0);
        // 返回最终统计的可达格子数
        return ret;
    }

    // DFS深度优先搜索:递归遍历所有合法可达的格子
    void dfs(int i, int j)
    {
        ret++;                // 到达一个合法格子,计数+1
        vis[i][j] = true;     // 标记当前坐标为已访问,避免重复遍历

        // 遍历仅有的两个方向:右、下
        for (int d = 0; d < 2; d++)
        {
            // 计算下一个坐标
            int x = i + dx[d], y = j + dy[d];
            // 核心判断条件:
            // 1. 坐标在棋盘范围内  2. 未被访问过  3. 满足数位和限制
            if (x >= 0 && x < m && y >= 0 && y < n && !vis[x][y] && check(x, y))
            {
                dfs(x, y);  // 递归访问下一个合法坐标
            }
        }
    }

    // 辅助函数:校验坐标(i,j)的数位之和是否 <= cnt
    bool check(int i, int j)
    {
        int tmp = 0;
        // 计算横坐标 i 的各位数字之和
        while (i)
        {
            tmp += i % 10;  // 取个位数字
            i /= 10;        // 去掉个位数字
        }
        // 计算纵坐标 j 的各位数字之和
        while (j)
        {
            tmp += j % 10;
            j /= 10;
        }
        // 返回数位和是否符合限制条件
        return tmp <= cnt;
    }
};

完整测试代码

cpp 复制代码
#include <iostream>
#include <cstring>
using namespace std;

class Solution
{
    int m, n, cnt;
    bool vis[101][101];
    int ret;
    int dx[2] = {0, 1};
    int dy[2] = {1, 0};

public:
    int wardrobeFinishing(int _m, int _n, int _cnt)
    {
        m = _m;
        n = _n;
        cnt = _cnt;
        ret = 0;
        memset(vis, false, sizeof(vis));

        if (!check(0, 0)) return 0;

        dfs(0, 0);
        return ret;
    }

    void dfs(int i, int j)
    {
        ret++;
        vis[i][j] = true;

        for (int d = 0; d < 2; d++)
        {
            int x = i + dx[d], y = j + dy[d];
            if (x >= 0 && x < m && y >= 0 && y < n && !vis[x][y] && check(x, y))
            {
                dfs(x, y);
            }
        }
    }

    bool check(int i, int j)
    {
        int tmp = 0;
        while (i)
        {
            tmp += i % 10;
            i /= 10;
        }
        while (j)
        {
            tmp += j % 10;
            j /= 10;
        }
        return tmp <= cnt;
    }
};

int main()
{
    Solution s;

    cout << "测试1: m = 4, n = 7, cnt = 5" << endl;
    cout << "结果: " << s.wardrobeFinishing(4, 7, 5) << endl;
    cout << "期望: 18" << endl;
    cout << "----------------------" << endl;

    cout << "测试2: m = 1, n = 1, cnt = 0" << endl;
    cout << "结果: " << s.wardrobeFinishing(1, 1, 0) << endl;
    cout << "期望: 1" << endl;
    cout << "----------------------" << endl;

    cout << "测试3: m = 2, n = 3, cnt = 1" << endl;
    cout << "结果: " << s.wardrobeFinishing(2, 3, 1) << endl;
    cout << "期望: 3" << endl;
    cout << "----------------------" << endl;

    cout << "测试4: m = 3, n = 1, cnt = 0" << endl;
    cout << "结果: " << s.wardrobeFinishing(3, 1, 0) << endl;
    cout << "期望: 1" << endl;
    cout << "----------------------" << endl;

    cout << "测试5: m = 10, n = 10, cnt = 2" << endl;
    cout << "结果: " << s.wardrobeFinishing(10, 10, 2) << endl;
    cout << "----------------------" << endl;

    return 0;
}


🚀真正的勇者不是流泪的人,而是含泪奔跑的人!


敬请期待下一篇文章内容:【递归、搜索与回溯算法】(掌握记忆化搜索的核心套路)


每日心灵鸡汤:先努力优秀,再大方拥有 !
很喜欢的一句话:"生活不需要比别人过得好,但一定要比以前过得好,人生最幸福的事,不是活的像别人,而是在努力之后活的更像自己."该努力的时候,别选择安逸,连小孩都知道,想要的东西要踮起脚尖自己伸手去拿,所以不要什么都不做,还什么都想要.世上没有不劳而获的东西,空想只会是一场空,不尝试、不努力,你永远不知道什么是拥有. 先努力优秀,再大方拥有.眼界不够,看到的都是问题,格局不够,纠结的都是鸡毛蒜皮.愿你熬过万丈孤苦,藏下星辰大海,半山腰总是最拥挤的,你得去山顶看看.

相关推荐
_小草鱼_2 小时前
【数据结构】链表
数据结构·链表·数组·单链表·双链表
黎梨梨梨_2 小时前
C++入门基础(上)(namespace和缺省参数)
开发语言·c++
谭欣辰2 小时前
字典树:高效字符串处理利器
c++·算法
俺爱吃萝卜2 小时前
Java 性能调优实战:从 JVM 内存模型到垃圾回收算法优化
java·jvm·算法
光电笑映2 小时前
深入C++异常:栈展开、异常安全与工程规范
开发语言·c++·c
鹿角片ljp2 小时前
LeetCode215: 数组中的第K个最大元素 —— 从快速选择到堆排
算法·排序算法
tankeven2 小时前
C++ 学习杂记00:标准模板库(STL)
c++
blog_wanghao2 小时前
条款03:尽可能使用const
c++
天若有情6732 小时前
用动态规划思路,一步一步实现响应式数据(从本质到落地)
算法·动态规划·代理模式