☰
LeetCode 749隔离病毒:图论与模拟算法解析
2026/10/15 7:56:11 网站建设 项目流程

1. 题目解析与解题思路

LeetCode 749题"隔离病毒"是一个典型的图论与模拟类题目,要求我们在二维网格上模拟病毒传播和隔离的过程。题目给定一个表示感染状态的矩阵isInfected,其中1表示被感染的区域,0表示健康区域。我们需要计算隔离所有病毒区域所需的最少防火墙数量。

1.1 问题建模

这道题本质上是在考察:

  • 网格中的连通区域识别(类似岛屿问题)
  • 模拟扩散过程(类似游戏生命问题)
  • 贪心算法策略(每次处理威胁最大的区域)

关键观察点:

  1. 病毒每晚向相邻的四个方向(上下左右)扩散
  2. 防火墙只能建在相邻的健康和感染区域之间
  3. 每天可以选择一个区域进行完全隔离
  4. 需要最小化防火墙总数

1.2 核心算法选择

经过分析,这个问题适合采用以下算法组合:

  1. BFS/DFS进行区域感染识别
  2. 优先队列选择威胁最大的区域
  3. 模拟多轮扩散和隔离过程
// 伪代码框架 public int containVirus(int[][] isInfected) { int walls = 0; while (存在活跃感染区) { 1. 识别所有感染区域及其威胁程度 2. 选择威胁最大的区域进行隔离 3. 计算该区域需要的防火墙数 4. 模拟其他区域的扩散 5. 更新感染状态 } return walls; }

2. 详细实现步骤

2.1 感染区域识别

我们需要为每个感染区域计算两个关键指标:

  1. 该区域的边界长度(决定防火墙数量)
  2. 该区域可能感染的健康细胞数量(决定威胁程度)
// 区域识别实现 private Region findMostThreateningRegion(int[][] grid) { int m = grid.length, n = grid[0].length; boolean[][] visited = new boolean[m][n]; Region maxRegion = null; for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { if (grid[i][j] == 1 && !visited[i][j]) { Region region = new Region(); dfs(grid, i, j, visited, region); if (maxRegion == null || region.threat > maxRegion.threat) { maxRegion = region; } } } } return maxRegion; }

2.2 防火墙计算与隔离

隔离一个区域需要:

  1. 标记该区域为已隔离(设为-1)
  2. 统计其与健康区域的相邻边数
private int isolateRegion(int[][] grid, Region region) { int walls = 0; for (int[] cell : region.infectedCells) { int i = cell[0], j = cell[1]; grid[i][j] = -1; // 标记为已隔离 // 检查四个方向 for (int[] dir : DIRECTIONS) { int x = i + dir[0], y = j + dir[1]; if (x >= 0 && x < grid.length && y >= 0 && y < grid[0].length && grid[x][y] == 0) { walls++; } } } return walls; }

2.3 病毒扩散模拟

每晚未被隔离的感染区域会向四周扩散:

private void spreadVirus(int[][] grid) { int m = grid.length, n = grid[0].length; List<int[]> newInfections = new ArrayList<>(); for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { if (grid[i][j] == 1) { for (int[] dir : DIRECTIONS) { int x = i + dir[0], y = j + dir[1]; if (x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == 0) { newInfections.add(new int[]{x, y}); } } } } } for (int[] cell : newInfections) { grid[cell[0]][cell[1]] = 1; } }

3. 完整算法实现

将上述模块组合起来,得到完整解决方案:

class Solution { private static final int[][] DIRECTIONS = {{-1,0}, {1,0}, {0,-1}, {0,1}}; class Region { List<int[]> infectedCells = new ArrayList<>(); Set<String> threatenedCells = new HashSet<>(); int threat = 0; int wallsRequired = 0; } public int containVirus(int[][] isInfected) { int walls = 0; while (true) { Region region = findMostThreateningRegion(isInfected); if (region == null) break; walls += isolateRegion(isInfected, region); spreadVirus(isInfected); } return walls; } // 前面定义的各个方法... }

4. 复杂度分析与优化

4.1 时间复杂度

设网格大小为m×n:

  • 每轮需要O(mn)时间扫描整个网格
  • 最多可能进行O(mn)轮(每次隔离至少一个单元格)
  • 因此最坏时间复杂度为O((mn)^2)

4.2 空间复杂度

主要消耗在:

  • 访问标记数组O(mn)
  • 区域存储O(mn)
  • 总体O(mn)

4.3 优化方向

  1. 并行处理:可以同时识别所有区域而非每次找最大
  2. 增量更新:只处理边界变化而非全图扫描
  3. 更高效的数据结构:如并查集管理感染区域

5. 测试用例与边界情况

5.1 典型测试用例

@Test public void testContainVirus() { Solution solution = new Solution(); // 基础用例 int[][] grid1 = { {0,1,0,0,0,0,0,1}, {0,1,0,0,0,0,0,1}, {0,0,0,0,0,0,0,1}, {0,0,0,0,0,0,0,0} }; assertEquals(10, solution.containVirus(grid1)); // 完全隔离 int[][] grid2 = {{1,1,1},{1,0,1},{1,1,1}}; assertEquals(4, solution.containVirus(grid2)); // 无感染 int[][] grid3 = {{0,0,0},{0,0,0},{0,0,0}}; assertEquals(0, solution.containVirus(grid3)); }

5.2 边界情况处理

  1. 网格为空或单元素
  2. 全部感染或全部健康
  3. 感染区域形成特殊形状(L型、环形等)
  4. 大规模网格(性能测试)

6. 常见错误与调试技巧

6.1 典型错误

  1. 无限循环:未正确判断终止条件
  2. 防火墙计数错误:重复计算或遗漏边界
  3. 扩散控制不当:新感染区域不应参与当晚扩散

6.2 调试建议

  1. 可视化中间状态:打印每轮后的网格
  2. 添加详细日志:记录每个区域的威胁值和防火墙数
  3. 单元测试:先确保各子模块正确性

关键提示:在扩散阶段,一定要先收集所有新感染位置再统一更新,避免同一轮中多次感染同一单元格。

7. 算法变种与扩展

7.1 问题变种

  1. 不同隔离成本:不同方向的防火墙成本不同
  2. 动态传播速率:不同区域传播速度不同
  3. 多类型病毒:需要区分不同病毒类型

7.2 实际应用场景

  1. 疫情传播控制模拟
  2. 网络安全中的威胁隔离
  3. 生态系统中物种扩散控制

8. 个人实现心得

在实际编码中,我发现以下几点特别重要:

  1. 使用单独的类管理区域信息比维护多个数据结构更清晰
  2. 在扩散阶段使用批量更新可以避免很多边界问题
  3. 威胁值的计算方式直接影响算法效率,需要仔细设计

一个容易忽略的细节是:已隔离区域(标记为-1)不应再参与后续处理。我在最初实现时因为这个错误导致结果偏大。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询