腐烂的橘子

题目要求

在一个 m x n 的网格 grid 中,每个单元格可能包含以下三种值之一:

  • 值 0 代表空单元格;
  • 值 1 代表新鲜橘子;
  • 值 2 代表腐烂的橘子。

每过一分钟,任何与腐烂橘子相邻(上下左右四个方向)的新鲜橘子都会变腐烂。

任务是计算并返回使网格中所有新鲜橘子变腐烂所需的最小分钟数。如果存在无法被腐烂的新鲜橘子,则返回 -1

解题思路

这个问题可以通过广度优先搜索(BFS)来解决。以下是解题步骤:

  1. 初始化队列和计数器:首先,遍历整个网格,将所有腐烂橘子的位置加入到队列中,并统计新鲜橘子的数量。

  2. 广度优先搜索:然后,进行 BFS,每次从队列中取出一个腐烂橘子的位置,查看其四周是否有新鲜橘子。如果有,则将新鲜橘子变为腐烂状态(即值从 1 变为 2),并将其位置加入队列中,同时新鲜橘子的数量减一。

  3. 时间计数:每完成一轮 BFS,表示过去了一分钟。在这一分钟内,所有与腐烂橘子相邻的新鲜橘子都会变腐烂。

  4. 检查新鲜橘子:当队列为空或新鲜橘子的数量变为 0 时,BFS 结束。如果还有新鲜橘子剩余,说明这些橘子无法被腐烂,返回 -1。否则,返回所用的分钟数。

  5. 边界条件处理:如果一开始就没有新鲜橘子,那么直接返回 0 分钟。

这个问题的关键在于如何有效地模拟橘子腐烂的过程,并且能够在所有橘子都腐烂或确定有橘子无法腐烂时停止搜索。通过 BFS,我们可以确保每一轮都是在模拟一分钟内腐烂的传播,这样可以准确计算出所需的最小分钟数。

Golang 版本

func orangesRotting(grid [][]int) int {
    rows, cols := len(grid), len(grid[0])
    freshCount := 0
    queue := make([][2]int, 0)

    // 初始化队列,统计新鲜橘子数量
    for r := 0; r < rows; r++ {
        for c := 0; c < cols; c++ {
            if grid[r][c] == 2 {
                queue = append(queue, [2]int{r, c})
            } else if grid[r][c] == 1 {
                freshCount++
            }
        }
    }

    // 如果没有新鲜橘子,直接返回0
    if freshCount == 0 {
        return 0
    }

    // 四个方向的移动向量
    directions := [][2]int{{-1, 0}, {1, 0}, {0, -1}, {0, 1}}
    minutesPassed := 0

    // 开始BFS
    for len(queue) > 0 {
        currentQueueSize := len(queue)
        for i := 0; i < currentQueueSize; i++ {
            orange := queue[0]
            queue = queue[1:]
            for _, d := range directions {
                newRow, newCol := orange[0]+d[0], orange[1]+d[1]
                // 检查新位置是否有效
                if newRow >= 0 && newRow < rows && newCol >= 0 && newCol < cols {
                    if grid[newRow][newCol] == 1 {
                        grid[newRow][newCol] = 2
                        freshCount--
                        queue = append(queue, [2]int{newRow, newCol})
                    }
                }
            }
        }
        // 完成一轮BFS,时间增加
        if len(queue) > 0 {
            minutesPassed++
        }
    }

    // 检查是否还有新鲜橘子
    if freshCount == 0 {
        return minutesPassed
    }
    return -1
}

这段代码定义了一个函数 orangesRotting,它接受一个二维整数数组 grid 作为输入,代表网格状态,并返回一个整数,表示使所有新鲜橘子变腐烂所需的最小分钟数。如果存在无法被腐烂的新鲜橘子,则返回 -1。代码使用广度优先搜索算法来模拟橘子腐烂的过程。

C++版本

#include <vector>
#include <queue>
using namespace std;

class Solution {
public:
    int orangesRotting(vector<vector<int>>& grid) {
        int rows = grid.size();
        int cols = grid[0].size();
        int freshCount = 0;
        queue<pair<int, int>> q;

        // 初始化队列,统计新鲜橘子数量
        for (int r = 0; r < rows; ++r) {
            for (int c = 0; c < cols; ++c) {
                if (grid[r][c] == 2) {
                    q.push({r, c});
                } else if (grid[r][c] == 1) {
                    freshCount++;
                }
            }
        }

        // 如果没有新鲜橘子,直接返回0
        if (freshCount == 0) return 0;

        // 四个方向的移动向量
        vector<pair<int, int>> directions{{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
        int minutesPassed = 0;

        // 开始BFS
        while (!q.empty()) {
            int currentQueueSize = q.size();
            for (int i = 0; i < currentQueueSize; ++i) {
                auto orange = q.front();
                q.pop();
                for (auto d : directions) {
                    int newRow = orange.first + d.first;
                    int newCol = orange.second + d.second;
                    // 检查新位置是否有效
                    if (newRow >= 0 && newRow < rows && newCol >= 0 && newCol < cols) {
                        if (grid[newRow][newCol] == 1) {
                            grid[newRow][newCol] = 2;
                            freshCount--;
                            q.push({newRow, newCol});
                        }
                    }
                }
            }
            // 完成一轮BFS,时间增加
            if (!q.empty()) {
                minutesPassed++;
            }
        }

        // 检查是否还有新鲜橘子
        if (freshCount == 0) {
            return minutesPassed;
        }
        return -1;
    }
};

这段 C++代码定义了一个类 Solution,其中包含一个方法 orangesRotting,它接受一个二维整数数组 grid 作为输入,并返回一个整数,表示使所有新鲜橘子变腐烂所需的最小分钟数。如果存在无法被腐烂的新鲜橘子,则返回 -1。代码使用广度优先搜索算法来模拟橘子腐烂的过程。

Python 版本

from collections import deque

class Solution:
    def orangesRotting(self, grid: List[List[int]]) -> int:
        rows, cols = len(grid), len(grid[0])
        fresh_count = 0
        queue = deque()

        # 初始化队列,统计新鲜橘子数量
        for r in range(rows):
            for c in range(cols):
                if grid[r][c] == 2:
                    queue.append((r, c))
                elif grid[r][c] == 1:
                    fresh_count += 1

        # 如果没有新鲜橘子,直接返回0
        if fresh_count == 0:
            return 0

        minutes_passed = 0
        directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]

        # 开始BFS
        while queue:
            for _ in range(len(queue)):
                r, c = queue.popleft()
                for dr, dc in directions:
                    nr, nc = r + dr, c + dc
                    # 检查新位置是否有效
                    if 0 <= nr < rows and 0 <= nc < cols and grid[nr][nc] == 1:
                        grid[nr][nc] = 2
                        fresh_count -= 1
                        queue.append((nr, nc))
            if queue:
                minutes_passed += 1

        # 检查是否还有新鲜橘子
        return minutes_passed if fresh_count == 0 else -1

这段 Python 代码定义了一个 Solution 类,其中包含一个方法 orangesRotting,它接受一个二维整数列表 grid 作为输入,并返回一个整数,表示使所有新鲜橘子变腐烂所需的最小分钟数。如果存在无法被腐烂的新鲜橘子,则返回 -1。代码使用广度优先搜索算法来模拟橘子腐烂的过程。

Java 版本

import java.util.*;

public class Solution {
    public int orangesRotting(int[][] grid) {
        if (grid == null || grid.length == 0) return 0;
        int rows = grid.length, cols = grid[0].length;
        Queue<int[]> queue = new LinkedList<>();
        int freshCount = 0;

        // 初始化队列,统计新鲜橘子数量
        for (int r = 0; r < rows; r++) {
            for (int c = 0; c < cols; c++) {
                if (grid[r][c] == 2) {
                    queue.offer(new int[]{r, c});
                } else if (grid[r][c] == 1) {
                    freshCount++;
                }
            }
        }

        // 如果没有新鲜橘子,直接返回0
        if (freshCount == 0) return 0;

        int minutesPassed = 0;
        int[][] directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};

        // 开始BFS
        while (!queue.isEmpty()) {
            int currentQueueSize = queue.size();
            for (int i = 0; i < currentQueueSize; i++) {
                int[] orange = queue.poll();
                for (int[] d : directions) {
                    int newRow = orange[0] + d[0];
                    int newCol = orange[1] + d[1];
                    // 检查新位置是否有效
                    if (newRow >= 0 && newRow < rows && newCol >= 0 && newCol < cols) {
                        if (grid[newRow][newCol] == 1) {
                            grid[newRow][newCol] = 2;
                            freshCount--;
                            queue.offer(new int[]{newRow, newCol});
                        }
                    }
                }
            }
            if (!queue.isEmpty()) {
                minutesPassed++;
            }
        }

        // 检查是否还有新鲜橘子
        return freshCount == 0 ? minutesPassed : -1;
    }
}

这段 Java 代码定义了一个 Solution 类,其中包含一个方法 orangesRotting,它接受一个二维整数数组 grid 作为输入,并返回一个整数,表示使所有新鲜橘子变腐烂所需的最小分钟数。如果存在无法被腐烂的新鲜橘子,则返回 -1。代码使用广度优先搜索算法来模拟橘子腐烂的过程。

Rust 版本

#![allow(unused)]
fn main() {
use std::collections::VecDeque;

pub fn oranges_rotting(grid: Vec<Vec<i32>>) -> i32 {
    let rows = grid.len();
    let cols = grid[0].len();
    let mut queue: VecDeque<(usize, usize)> = VecDeque::new();
    let mut fresh_count = 0;

    // 初始化队列,统计新鲜橘子数量
    for r in 0..rows {
        for c in 0..cols {
            match grid[r][c] {
                2 => queue.push_back((r, c)),
                1 => fresh_count += 1,
                _ => (),
            }
        }
    }

    // 如果没有新鲜橘子,直接返回0
    if fresh_count == 0 {
        return 0;
    }

    let mut minutes_passed = 0;
    let directions = [(-1, 0), (1, 0), (0, -1), (0, 1)];

    // 开始BFS
    while let Some((r, c)) = queue.pop_front() {
        let mut added = false;
        for d in directions.iter() {
            let (dr, dc) = *d;
            let (new_r, new_c) = (r as i32 + dr, c as i32 + dc);
            if new_r >= 0 && new_r < rows as i32 && new_c >= 0 && new_c < cols as i32 {
                let (new_r, new_c) = (new_r as usize, new_c as usize);
                if grid[new_r][new_c] == 1 {
                    grid[new_r][new_c] = 2;
                    fresh_count -= 1;
                    queue.push_back((new_r, new_c));
                    added = true;
                }
            }
        }
        if added {
            minutes_passed += 1;
        }
    }

    // 检查是否还有新鲜橘子
    if fresh_count == 0 { minutes_passed } else { -1 }
}
}

这段 Rust 代码定义了一个函数 oranges_rotting,它接受一个二维向量 grid 作为输入,并返回一个整数,表示使所有新鲜橘子变腐烂所需的最小分钟数。如果存在无法被腐烂的新鲜橘子,则返回 -1。代码使用广度优先搜索算法来模拟橘子腐烂的过程。注意,Rust 中的索引必须是 usize 类型,因此在处理索引时需要进行类型转换。

总结

上面的解法采用了广度优先搜索(BFS)算法来解决“腐烂的橘子”问题。这个问题可以被看作是在一个二维网格上的感染扩散模型,其中橘子可以处于新鲜状态(1)或腐烂状态(2),空单元格(0)不参与感染过程。

解法的关键步骤如下:

  1. 初始化队列和统计新鲜橘子: 遍历整个网格,将所有腐烂的橘子的位置加入队列,并统计新鲜橘子的数量。

  2. 特殊情况处理: 如果没有新鲜橘子,直接返回 0,因为不需要时间来腐烂。

  3. 广度优先搜索(BFS): 使用队列来进行 BFS,每次从队列中取出一个腐烂的橘子的位置,然后查看其上下左右的邻居。如果邻居是新鲜橘子(1),则将其腐烂(变为 2),并将其位置加入队列,同时减少新鲜橘子的数量。

  4. 时间计数: 每完成一轮队列中的橘子处理,时间增加 1。这模拟了每分钟腐烂橘子影响其邻居的过程。

  5. 检查是否还有新鲜橘子: 完成 BFS 后,如果还有新鲜橘子,说明它们无法被腐烂的橘子感染到,返回-1。否则,返回所需的分钟数。

这个解法的时间复杂度通常是 O(mn),其中 m 是网格的行数,n 是网格的列数,因为每个单元格最多只会被访问一次。空间复杂度也是 O(mn),主要是因为在最坏的情况下,可能需要存储整个网格中所有的橘子位置。