【Hot 100】994. 腐烂的橘子

请添加图片描述

  • 🙋‍♂️ 作者:海码007
  • 📜 专栏:算法专栏
  • 💥 标题:【Hot 100】994. 腐烂的橘子
  • ❣️ 寄语:书到用时方恨少,事非经过不知难!

引言

继续图论专题。

腐烂的橘子

  • 🎈 题目链接:
  • 🎈 做题状态:

我的解题

这道题应该使用 BFS 而不是 DFS ,因为每一层的时间(或者叫做步数)是需要一致的。如果是 DFS 的话每一层的步调不一致。

BFS 可以看成是层序遍历。从某个结点出发,BFS 首先遍历到距离为 1 的结点,然后是距离为 2、3、4…… 的结点。因此,BFS 可以用来求最短路径问题。BFS 先搜索到的结点,一定是距离最近的结点。

再看看这道题的题目要求:返回直到单元格中没有新鲜橘子为止所必须经过的最小分钟数。翻译一下,实际上就是求腐烂橘子到所有新鲜橘子的最短路径。那么这道题使用 BFS,应该是毫无疑问的了。

这道题的主要思路是:

  • 一开始,我们找出所有腐烂的橘子,将它们放入队列,作为第 0 层的结点。
  • 然后进行 BFS 遍历,每个结点的相邻结点可能是上、下、左、右四个方向的结点,注意判断结点位于网格边界的特殊情况。
  • 由于可能存在无法被污染的橘子,我们需要记录新鲜橘子的数量。在 BFS 中,每遍历到一个橘子(污染了一个橘子),就将新鲜橘子的数量减一。如果 BFS 结束后这个数量仍未减为零,说明存在无法被污染的橘子。
class Solution {
public:
    int orangesRotting(vector<vector<int>>& grid) {
        int m = grid.size();
        int n = grid[0].size();
        queue<pair<int, int>> q;

        int count = 0;  // 记录新鲜橘子的数量
        // 遍历格网,将所有的烂橘子作为第一层加入到队列中
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                if (grid[i][j] == 1) {
                    ++count;
                } else if (grid[i][j] == 2) {
                    q.push({i, j});
                }
            }
        }

        int round = 0;  // 表示水果腐烂的轮数
        // 定义四个方向的偏移量:上、下、左、右
        vector<pair<int, int>> directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};

        while (!q.empty() && count > 0) {
            ++round;
            int size = q.size();   // 记录当前层所有腐败的水果

            // 弹出当前层所有的腐败水果
            for (int i = 0; i < size; ++i) {
                auto [x, y] = q.front(); q.pop();

                for (auto [dx, dy] : directions) {
                    int nx = x + dx;
                    int ny = y + dy;
                    // 检查新坐标是否在网格范围内且是新鲜橘子
                    if (nx >= 0 && nx < m && ny >= 0 && ny < n && grid[nx][ny] == 1) {
                        grid[nx][ny] = 2;
                        --count;
                        q.push({nx, ny});
                    }
                }
            }
        }

        // 判断腐败完是否还有新鲜水果
        if (count > 0)
        {
            return -1;
        } else
        {
            return round;
        }
    }
};

BFS 和 DFS 的适用场景及选择策略

1. BFS(广度优先搜索)

核心思想:按“层”遍历,先访问离起点最近的节点,再逐步向外扩展。
数据结构:队列(FIFO)。
适用场景

  • 最短路径问题(无权图或所有边权重相同):
    • 如“腐烂的橘子”、“迷宫的最短路径”、“单词接龙”等。
    • 因为 BFS 保证第一次到达目标时的路径一定是最短的。
  • 分层遍历或按距离处理
    • 如“二叉树的层序遍历”、“岛屿数量”等需要逐层扩散的问题。
  • 拓扑排序
    • 如“课程表”等依赖关系问题,可以用 BFS(Kahn 算法)实现。
  • 状态空间搜索(步数最小化)
    • 如“滑动谜题”、“八数码问题”等需要最少步数的问题。

不适用场景

  • 问题不需要最短路径,且图/树深度很大(可能导致队列内存爆炸)。
  • 需要递归或回溯的问题(如全排列、组合问题)。

2. DFS(深度优先搜索)

核心思想:一条路走到底,回溯后再尝试其他路径。
数据结构:栈(递归或显式栈)。
适用场景

  • 连通性问题
    • 如“岛屿的最大面积”、“图的连通分量”等,DFS 可以轻松标记所有连通区域。
  • 回溯问题
    • 如“全排列”、“组合总和”、“N 皇后”等需要尝试所有可能解的问题。
  • 拓扑排序
    • DFS 也可以实现(通过后序遍历反转)。
  • 路径问题(不关心最短路径)
    • 如“二叉树的所有路径”、“图的简单路径”等。

不适用场景

  • 需要最短路径的问题(DFS 可能绕远路)。
  • 图/树非常宽(递归栈可能溢出)。

3. 如何选择 BFS 或 DFS?

问题特征选择 BFS选择 DFS
需要最短路径/最小步数✅(如迷宫、单词接龙)❌(可能绕路)
需要遍历所有可能解❌(队列内存消耗大)✅(如全排列、组合问题)
图/树深度很大❌(队列可能爆内存)✅(递归栈可控)
图/树宽度很大✅(按层处理)❌(递归栈可能溢出)
需要快速找到一个解✅(首次到达即最优)✅(可能更快碰运气)
需要记录路径❌(需额外存储路径)✅(回溯天然记录路径)

4. 经典例题对比

题目BFS 解法DFS 解法
二叉树层序遍历✅ 天然分层遍历❌ 需记录深度,不如 BFS 直观
岛屿数量✅ 逐层扩散标记✅ 递归标记连通区域
单词接龙(最短转换序列)✅ 最短路径❌ 可能超时
全排列❌ 需维护状态队列,复杂✅ 回溯天然适合
腐烂的橘子✅ 最短时间扩散❌ 无法保证最短时间

5. 总结

  • 用 BFS 如果
    • 需要最短路径、分层处理、状态空间步数最小化。
    • 问题类似“扩散”、“最少步数”、“层序遍历”。
  • 用 DFS 如果
    • 需要递归/回溯、遍历所有解、连通性分析。
    • 问题类似“所有可能”、“连通区域”、“排列组合”。

灵活结合:某些问题可以同时用 BFS 和 DFS(如“岛屿数量”),但要根据问题特点选择更优解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值